Rešitev

Ciljanje

Rešitev, ki uporablja, kar smo se učili na predavanjih v tednu po roku za oddajo naloge, je takšna.

from math import *
from random import *

cilj = randint(10, 50)
print("Cilj je", cilj, "daleč. Uspešno streljajte.")

razdalja = 0
while abs(cilj - razdalja) > 1:
    hitrost = float(input("Hitrost: "))
    kot = radians(float(input("Kot: ")))
    razdalja = (hitrost ** 2) * sin(2 * kot) / 9.81
    if abs(cilj - razdalja) > 1:
        print("Mimo: kroglo je neslo", razdalja, "daleč.")
print("Bum.")

V pogoju zanke preverjamo, ali je razdalja, ki jo preleti krogla, za manj kot 1 različna od razdalje do cilja. Z uporabo abs smo se izognili temu, da bi morali pisati pogoje v slogu

while razdalja > cilj - 1 and razdalja < cilj + 1:

ali kaj podobnega.

Ker uporabnika sprašujemo šele v zanki in torej tudi razdalja računamo šele v njej, pred zanko nastavimo razdalja na neko vrednost, ki je gotovo napačna, v našem primeru 0.

Malo moteče je, da dvakrat računamo abs(cilj - razdalja) > 1. Rešimo se, recimo, z nečim, česar na drugih predavanjih še nisem hotel omeniti, da se ne bi prehitro razvadili: zanko lahko prekinemo z break.

from math import *
from random import *

cilj = randint(10, 50)
print("Cilj je", cilj, "daleč. Uspešno streljajte.")

while True:
    hitrost = float(input("Hitrost: "))
    kot = radians(float(input("Kot: ")))
    razdalja = (hitrost ** 2) * sin(2 * kot) / 9.81
    if abs(cilj - razdalja) <= 1:
        break
    print("Mimo: kroglo je neslo", razdalja, "daleč.")
print("Bum.")

Pogoj v zanki je zdaj kar while True: zanka naj se vrti v nedogled. No, skoraj: če je cilj zadet, jo prekinemo z break.

V študentskih izdelkihh sem pogosto videval "zastavice". Takole

from math import *
from random import *

cilj = randint(10, 50)
print("Cilj je", cilj, "daleč. Uspešno streljajte.")

zadet = False
while not zadet:
    hitrost = float(input("Hitrost: "))
    kot = radians(float(input("Kot: ")))
    razdalja = (hitrost ** 2) * sin(2 * kot) / 9.81
    if abs(cilj - razdalja) <= 1:
        zadet = True
    else:
        print("Mimo: kroglo je neslo", razdalja, "daleč.")
print("Bum.")

Tudi prav. Z break-om se jim sicer lahko navadno izognemo.

Nekateri ste pisali while zadet == False. To je manj lepo. Pravzaprav ni lepo. :)

Prašič in cilj: rocket science. :)

Najprej vprašamo uporabnika, kje sta prašič in cilj.

xp = float(input("Razdalja do prašiča: "))
yp = float(input("Višina prašiča: "))
xc = float(input("Razdalja do cilja: "))

Potem si premislimo in ne sprašujemo uporabnika, da ne bo potrebno stalno vpisovati številk, ko bomo popravljali program. Številke kar vprogramiramo; ko bomo popravljali program, jih bomo malo spreminjali, na koncu pa spet vrnemo input-e.

Rešitev z malo poskušanja

from math import *
g = 9.81

xp = 2
yp = 8
xc = 10

kot = 0
while kot < 90:
    kot += 0.1
    radkot = radians(kot)
    v = sqrt(xc * g / sin(2 * radkot))
    vx = v * cos(radkot)
    vy = v * sin(radkot)
    t = xp / vx
    visina = vy * t - g * t ** 2 / 2
    if abs(yp - visina < 0.1:
        print(kot, v)

Program preskusi različne kote, s korakom 0.1 stopinje. Izpisal bo vse kombinacije (kot, hitrost), ki zadanejo cilj in prašiča.

V zanko počnemo tole. Ko si izberemo kot, lahko iz tega izračunamo potrebno hitrost. Le formulo iz obvezne domače naloge moramo preobrniti: tam smo iz kota in hitrosti računali razdaljo, tu pa iz kota in razdalje računamo hitrost.

Ko imamo hitrost (v), jo razstavimo na vodoravno in navpično komponento. Krogla se bo v začetku pomikala s hitrostjo vx v vodoravni smeri in vy navzgor. Vodoravno hitrost bo obdržala, torej lahko izračunamo, koliko časa bo potrebovala do prašiča -- v vodoravni smeri: t = xp / vx. V času t bo torej nad, pod ali v prašiču. Izračunati moramo le še, kako visoko bo v tem trenutku. Iz fizike vemo, da je pot pri pospešenem gibanju enaka $$s = v t + a t^2 / 2,$$ kjer je $v$ začetna hitrost, $a$ pa pospešek. Pri nas kažeta začetna hitrost in pospešek v različni smeri, poleg tega pa opazujemo le navpično komponento, zato vy * t - g * t ** 2 / 2.

To je to.

Računska rešitev

Naj bo top torej v točki $(0, 0)$, prašič v $(x_p, y_p)$ in cilj na $(x_c, 0)$.

Vemo, da krogla leti po paraboli, torej funkciji z enačbo

$$y = ax^2 + b^x + c.$$.

Parabola mora teči skozi zgornje točke. Njihove $x$ in $y$ vstavimo v enačbo in dobimo sistem enačb.

$$0 = a 0^2 + b 0 + c$$ $$y_p = a x_p^2 + bx_p + c$$ $$0 = a x_c^2 + bx_c + c$$

Prva enačba pove, da je $c=0$. Ostaneta le še drugi dve, brez $c$jev.

$$y_p = a x_p^2 + bx_p$$ $$0 = a x_c^2 + bx_c$$

Zadnjo delimo z $x_c$ (to smemo gotovo storiti, saj vemo da $x_c\ne 0,$ razen če smo topničarju ukazali, naj naredi harakiri s topom). Potem jo obrnemo v $b = -ax_c$.

To nesemo v drugo enačbo in imamo

$$ y_p = a x_p^2 - ax_c x_p. $$

Izpostavimo $a$ in delimo z $x_p^2 - x_c x_p$, pa imamo

$$ a = \frac{y_p}{x_p^2 - x_c x_p}.$$

In, ker je $b = -ax_c$, je

$$ b = -\frac{y_p x_c}{x_p^2 - x_c x_p},$$

oziroma

$$ b = -\frac{y_p x_c}{x_c x_p - x_p^2}.$$

Iz podatkov torej znamo izračunati enačbo parabole. Topničar pa potrebuje kot in hitrost. Matematika nas uči, da je tangens kota enak odvoda v tej točki. Če imamo $y = ax^2 + bx + c,$ je odvod enak

$$y' = ax + b.$$

Zanima nas odvod v izhodišču. Vstavimo $x=0$, dobimo $y' = b$. Kot je potem

$$\phi = \arctan b = \arctan \frac{y_p x_c}{x_c x_p - x_p^2}.$$

Hitrost pa, spet, dobimo tako, da preobrnemo formulo $x_c = \frac{v^2 \sin(2\phi)}{g},$ ki je bila podana ob nalogi, tako da izrazimo $v$:

$$v = \sqrt{\frac{x_c g }{\sin 2\phi}}.$$

Rešitev je torej:

from math import *

xp = float(input("Razdalja do prašiča: "))
yp = float(input("Višina prašiča: "))
xc = float(input("Razdalja do cilja: "))

phi = atan((yp * xc) / (xp * xc - xp ** 2))
v = sqrt(cilj * g / sin(2 * phi))
print(degrees(phi), v)
Zadnja sprememba: torek, 12. oktober 2021, 11.47