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)