4. Podmienky¶
Podmienený príkaz¶
Pri programovaní často riešime situácie, keď sa má program na základe nejakej podmienky rozhodnúť medzi viacerými možnosťami. Napríklad, program má vypísať, či zadaný počet bodov stačí na známku z predmetu. Preto si najprv vyžiada číslo - získaný počet bodov, potom porovná túto hodnotu s požadovanou hranicou, napríklad 100 bodov a na základe toho vypíše, buď že je to dosť na známku, alebo nie je:
body = int(input('Zadaj získaný počet bodov: '))
if body >= 110:
print(body, 'bodov je dostačujúci počet na známku')
else:
print(body, 'bodov je málo na získanie známky')
Použili sme tu podmienený príkaz (príkaz vetvenia) if
. Jeho zápis vyzerá takto:
if podmienka: # ak podmienka platí, vykonaj 1. skupinu príkazov
prikaz
prikaz
...
else: # ak podmienka neplatí, vykonaj 2. skupinu príkazov
prikaz
prikaz
...
V našom prvom príklade je v oboch skupinách príkazov len po jednom príkaze print()
. Odsadenie skupiny príkazov (blok príkazov) má rovnaký význam ako vo for-cykle: budeme ich odsadzovať vždy presne o 4 medzery.
V pravidlách predmetu programovanie máme takéto kritériá na získanie známky:
známka A aspoň 150 bodov
známka B aspoň 140 bodov
známka C aspoň 130 bodov
známka D aspoň 120 bodov
známka E aspoň 110 bodov
známka Fx menej ako 110 bodov
Podmienka pre získanie známky A:
if body >= 150:
print('za', body, 'bodov získavaš známku A')
else:
...
Ak je bodov menej ako 150, už to môže byť len horšia známka: dopíšeme testovanie aj známky B:
if body >= 150:
print('za', body, 'bodov získavaš známku A')
else:
if body >= 140:
print('za', body, 'bodov získavaš známku B')
else:
...
Všetky riadky v druhej skupine príkazov (za else
) musia byť odsadené o 4 medzery, preto napríklad print()
, ktorý vypisuje správu o známke B je odsunutý o 8 medzier. Podobným spôsobom zapíšeme všetky zvyšné podmienky:
body = int(input('Zadaj získaný počet bodov: '))
if body >= 150:
print('za', body, 'bodov získavaš známku A')
else:
if body >= 140:
print('za', body, 'bodov získavaš známku B')
else:
if body >= 130:
print('za', body, 'bodov získavaš známku C')
else:
if body >= 120:
print('za', body, 'bodov získavaš známku D')
else:
if body >= 110:
print('za', body, 'bodov získavaš známku E')
else:
print('za', body, 'bodov si nevyhovel a máš známku Fx')
Takéto odsadzovanie príkazov je v Pythone veľmi dôležité a musíme byť pritom veľmi presní. Príkaz if
, ktorý sa nachádza vo vnútri niektorej vetvy iného if
, sa nazýva vnorený príkaz if.
V Pythone existuje konštrukcia, ktorá uľahčuje takúto vnorenú sériu if
-ov:
if podmienka_1: # ak podmienka_1 platí, vykonaj 1. skupinu príkazov
prikaz
...
elif podmienka_2: # ak podmienka_1 neplatí, ale platí podmienka_2, ...
prikaz
...
elif podmienka_3: # ak ani podmienka_1 ani podmienka_2 neplatia, ale platí podmienka_3, ...
prikaz
...
else: # ak žiadna z podmienok neplatí, ...
prikaz
...
Predchádzajúci program môžeme zapísať aj takto:
body = int(input('Zadaj získaný počet bodov: '))
if body >= 150:
print('za', body, 'bodov získavaš známku A')
elif body >= 140:
print('za', body, 'bodov získavaš známku B')
elif body >= 130:
print('za', body, 'bodov získavaš známku C')
elif body >= 120:
print('za', body, 'bodov získavaš známku D')
elif body >= 110:
print('za', body, 'bodov získavaš známku E')
else:
print('za', body, 'bodov si nevyhovel a máš známku Fx')
Ukážme ešte jedno riešenie tejto úlohy - jednotlivé podmienky zapíšeme ako intervaly:
body = int(input('Zadaj získaný počet bodov: '))
if body >= 150:
print('za', body, 'bodov získavaš známku A')
if 140 <= body < 150:
print('za', body, 'bodov získavaš známku B')
if 130 <= body < 140:
print('za', body, 'bodov získavaš známku C')
if 120 <= body < 130:
print('za', body, 'bodov získavaš známku D')
if 110 <= body < 120:
print('za', body, 'bodov získavaš známku E')
if body < 110:
print('za', body, 'bodov si nevyhovel a máš známku Fx')
V tomto riešení využívame to, že else
-vetva v príkaze if
môže chýbať, a teda pri neplatnej podmienke sa nevykoná nič:
if podmienka: # ak podmienka platí, vykonaj skupinu príkazov
prikaz
prikaz
...
# ak podmienka neplatí, nevykonaj nič
Mohli by sme to zapísať aj takto:
if podmienka: # ak podmienka platí, vykonaj skupinu príkazov
prikaz
prikaz
...
else:
pass # ak podmienka neplatí, nevykonaj nič
kde pass
je tzv. prázdny príkaz, t.j. príkaz, ktorý nerobí nič. Môžeme ho použiť všade, kde chceme zapísať tzv. prázdny blok príkazov. Hoci je tento zápis správny, nezvykne sa takto používať.
Zrejme každý príkaz if
po kontrole podmienky (a prípadnom výpise správy) pokračuje na ďalšom príkaze, ktorý nasleduje za ním (a má rovnaké odsadenie ako if
). Okrem toho vidíme, že teraz sú niektoré podmienky trochu zložitejšie, lebo testujeme, či sa hodnota nachádza v nejakom intervale. (podmienku 140 <= body < 150
sme mohli zapísať aj takto 150 > body >= 140
)
V Pythone môžeme zapisovať podmienky podobne, ako je to bežné v matematike:
|
je menšie ako |
|
je menšie alebo rovné |
|
rovná sa |
|
nerovná sa |
|
je väčšie ako |
|
je väčšie alebo rovné |
|
je väčšie ako … a zároveň menšie alebo rovné … |
|
|
Ukážme použitie podmieneného príkazu aj v grafickom programe. Začneme s programom, ktorý na náhodné pozície nakreslí 100 rôzne veľkých štvorčekov. Aj veľkosť týchto štvorčekov bude náhodné číslo od 1 do 30. Štvorčeky bude zafarbovať podľa takéhoto pravidla:
ak je ich veľkosť menšia alebo rovná 10, budú červené
inak, ak ich veľkosť menšia alebo rovná 20, budú modré
inak nebudú zafarbené, t.j. ich farba výplne bude
''
Zrejme v poslednej skupine štvorcov budú len tie, ktoré sú väčšie ako 20. Zapíšme program:
import tkinter
import random
canvas = tkinter.Canvas()
canvas.pack()
for i in range(100):
x = random.randint(1, 350)
y = random.randint(1, 230)
a = random.randint(1, 30)
if a <= 10:
farba = 'red'
elif a <= 20:
farba = 'blue'
else:
farba = ''
canvas.create_rectangle(x, y, x + a, y + a, fill=farba)
tkinter.mainloop()
Aj v tomto programe využívame sériu if
- elif
- else
, rovnako ako pri prepočítavaní bodov na známky v predchádzajúcom príklade. Dostávame
Pokračujme s grafickým programom, ktorý na náhodné pozície nakreslí 1000 malých krúžkov:
import tkinter
import random
canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()
for i in range(1000):
x = random.randint(1, 300)
y = random.randint(1, 300)
farba = 'blue'
canvas.create_oval(x - 5, y - 5, x + 5, y + 5, fill=farba, width=0)
tkinter.mainloop()
Všimnite si, že krúžky sú bez obrysu (doteraz mali kruhy čierny tenký obrys). Je to vďaka pomenovanému parametru width=0
, ktorým určujeme hrúbku obrysu, teda teraz bude 0
.
Niektoré z týchto krúžkov zafarbíme tak, že tie z nich, ktoré sú v ľavej polovici plochy budú červené a zvyšné v pravej polovici (teda else
vetva) budú modré:
import tkinter
import random
canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()
for i in range(1000):
x = random.randint(1, 300)
y = random.randint(1, 300)
if x < 150:
farba = 'red'
else:
farba = 'blue'
canvas.create_oval(x - 5, y - 5, x + 5, y + 5, fill=farba, width=0)
tkinter.mainloop()
Skúsme pridať ešte jednu podmienku: všetky bodky v spodnej polovici (y > 150
) budú zelené, takže rozdelenie na červené a modré bude len v hornej polovici. Jedno z možných riešení:
import tkinter
import random
canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()
for i in range(1000):
x = random.randint(1, 300)
y = random.randint(1, 300)
if y < 150:
if x < 150:
farba = 'red'
else:
farba = 'blue'
else:
farba = 'green'
canvas.create_oval(x - 5, y - 5, x + 5, y + 5, fill=farba, width=0)
tkinter.mainloop()
Podobne, ako sme to robili s intervalmi bodov pre rôzne známky, môžeme aj toto riešenie zapísať tak, že použijeme komplexnejšiu podmienku:
import tkinter
import random
canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()
for i in range(1000):
x = random.randint(1, 300)
y = random.randint(1, 300)
if y < 150 and x < 150:
farba = 'red'
elif y < 150 and x >= 150:
farba = 'blue'
else:
farba = 'green'
canvas.create_oval(x - 5, y - 5, x + 5, y + 5, fill=farba, width=0)
tkinter.mainloop()
Podmienky v Pythone môžu obsahovať logické operácie a tieto majú obvyklý význam z matematiky:
podmienka1
and
podmienka2 … (a súčasne) znamená, že musia platiť obe podmienkypodmienka1
or
podmienka2 … (alebo) znamená, že musí platiť aspoň jedna z podmienoknot
podmienka … (neplatí) znamená, že daná podmienka neplatí
Otestovať rôzne kombinácie podmienok môžeme, napríklad takto:
>>> a = 10
>>> b = 7
>>> a < b
False
>>> a >= b + 3
True
>>> b < a < 2 * b
True
>>> a != 7 and b == a - 3
True
>>> a == 7 or b == 10
False
>>> not a == b # to isté ako a != b
True
>>> 1 == '1'
False
>>> 1 < '2' # nemôžeme takto porovnaváť čísla a znaky
...
TypeError: unorderable types: int() < str()
Všimnite si, že podmienky, ktoré platia, majú hodnotu True
a ktoré neplatia, majú False
- sú to dve špeciálne hodnoty, ktoré Python používa ako výsledky porovnávania - tzv. logických výrazov. Sú logického typu, tzv. bool
. Môžeme to skontrolovať:
>>> type(1 + 2)
<class 'int'>
>>> type(1 / 2)
<class 'float'>
>>> type('12')
<class 'str'>
>>> type(1 < 2)
<class 'bool'>
Aj logické hodnoty môžeme medzi sebou porovnávať. Zamyslite sa nad takouto podmienkou v if
:
import tkinter
import random
canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()
for i in range(1000):
x = random.randint(1, 300)
y = random.randint(1, 300)
if (y < 150) == (x < 150):
farba = 'red'
else:
farba = 'blue'
canvas.create_oval(x - 5, y - 5, x + 5, y + 5, fill=farba, width=0)
tkinter.mainloop()
Logické operácie¶
Pozrime sa podrobnejšie na logické operácie and
, or
a not
. Tieto operácie samozrejme fungujú pre logické hodnoty True
a False
.
A |
B |
A and B |
|
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A |
B |
A or B |
|
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A |
not A |
|
---|---|---|
|
|
|
|
|
Logické operácie fungujú nielen pre logický typ, ale aj pre skoro všetky ďalšie typy. V tomto prípade Python pre každý typ definuje prípady, ktoré on (Python) chápe ako False
a zrejme všetky ostatné hodnoty tohto typu chápe ako True
. Ukážme prípady pre doteraz známe typy, ktoré označujú logickú hodnotu False
:
typ |
|
|
|
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
Tejto tabuľke budeme rozumieť takto: keď bude Python niekde očakávať logickú hodnotu (napríklad v príkaze if
) a bude tam namiesto toho celé číslo, tak, ak má hodnotu 0, pochopí to ako False
a ak má hodnotu rôznu od 0, bude to pre neho znamenať True
. Podobne aj s reťazcami: ak ako podmienku if
uvedieme znakový reťazec, tento bude reprezentovať False
, ak je prázdny a True
, ak je neprázdny.
Napríklad:
pocet = int(input('zadaj: '))
if pocet:
print('pocet je rôzny od 0')
else:
print('pocet je 0')
meno = input('zadaj: ')
if meno:
print('meno nie je prázdny reťazec')
else:
print('meno je prázdny reťazec')
Logické operácie and
, or
a not
majú v skutočnosti tiež trochu rozšírenú interpretáciu:
Môžeme upraviť aj tabuľku pravdivostných hodnôt:
A |
B |
A and B |
|
---|---|---|---|
|
hocičo |
A |
|
|
hocičo |
B |
Tabuľka:
A |
B |
A or B |
|
---|---|---|---|
|
hocičo |
B |
|
|
hocičo |
A |
Napríklad:
>>> 1 + 2 and 3 + 4 # keďže 1+2 nie je False, výsledkom je 3+4
7
>>> 'ahoj' or 'Python' # keďže 'ahoj' nie je False, výsledkom je 'ahoj'
'ahoj'
>>> '' or 'Python' # keďže '' je False, výsledkom je 'Python'
'Python'
>>> 3 < 4 and 'kuk' # keďže 3<4 nie je False, výsledkom je 'kuk'
'kuk'
>>> False or True # keďže False je False, výsledkom je True
True
>>> 'False' or 'True' # keďže 'False' nie je False, výsledkom je 'False'
'False'
Podmienený príkaz sa často používa pri náhodnom rozhodovaní. Napríklad, hádžeme mincou (náhodné hodnoty 0 a 1) a ak padne 1, kreslíme náhodnú kružnicu, inak nakreslíme náhodný štvorec. Toto opakujeme 10-krát:
import tkinter
import random
canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()
for i in range(10):
x = random.randint(1, 300)
y = random.randint(1, 300)
a = random.randint(5, 50)
if random.randrange(2): # t.j. random.randrange(2) != 0
canvas.create_oval(x - a, y - a, x + a, y + a)
else:
canvas.create_rectangle(x - a, y - a, x + a, y + a)
tkinter.mainloop()
Približne rovnaké výsledky by sme dostali, ak by sme hádzali kockou so 6 možnosťami (random.randint(1, 6)
) a pre čísla 1, 2, 3 by sme kreslili kružnicu, inak štvorec.
Túto ideu môžeme využiť aj pre takúto úlohu: vygenerujte 1000 farebných štvorčekov - modré a červené, pričom ich pomer je 1:50, t.j. na 50 červených štvorčekov pripadne približne 1 modrý:
import tkinter
import random
canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()
for i in range(1000):
x = random.randint(1, 300)
y = random.randint(1, 300)
if random.randrange(50): # t.j. random.randrange(50) != 0
farba = 'red'
else:
farba = 'blue'
canvas.create_rectangle(x - 5, y - 5, x + 5, y + 5, fill=farba, width=0)
tkinter.mainloop()
Delitele čísel¶
Nasledovný príklad zisťuje, akých deliteľov má zadané číslo:
cislo = int(input('Zadaj číslo: '))
pocet = 0
print('delitele:', end=' ')
for delitel in range(1, cislo + 1):
if cislo % delitel == 0: # mohli by sme zapísať aj if not cislo % delitel:
pocet += 1
print(delitel, end=' ')
print()
print('počet deliteľov:', pocet)
Výstup môže byť napríklad takýto:
Zadaj číslo: 100
delitele: 1 2 4 5 10 20 25 50 100
počet deliteľov: 9
Malou modifikáciou tejto úlohy vieme vytvoriť ďalšie dva programy. Prvý zisťuje, či je zadané číslo prvočíslo:
cislo = int(input('Zadaj číslo: '))
pocet = 0
for delitel in range(1, cislo + 1):
if cislo % delitel == 0:
pocet += 1
if pocet == 2:
print(cislo, 'je prvočíslo')
else:
print(cislo, 'nie je prvočíslo')
Po spustení, napríklad:
Zadaj číslo: 101
101 je prvočíslo
Ďalší program zisťuje, či je nejaké číslo dokonalé, t.j. súčet všetkých deliteľov menších ako samotné číslo sa rovná samotnému číslu. Na základe tohto nájde (postupne preverí) všetky dokonalé čísla do 10000:
print('dokonalé čísla do 10000 sú', end=' ')
for cislo in range(1, 10001):
sucet = 0
for delitel in range(1, cislo):
if cislo % delitel == 0:
sucet += delitel
if sucet == cislo:
print(cislo, end=', ')
print()
print('=== viac ich už nie je ===')
Program vypíše:
dokonalé čísla do 10000 sú 6, 28, 496, 8128,
=== viac ich už nie je ===
Vráťme sa k programu, ktorý zisťuje, či je nejaké číslo prvočíslo:
cislo = int(input('Zadaj číslo: '))
pocet = 0
for delitel in range(1, cislo + 1):
if cislo % delitel == 0:
pocet += 1
if pocet == 2:
print(cislo, 'je prvočíslo')
Tento for-cyklus prejde vždy cislo
-krát prechodov - bude kontrolovať, či je cislo
deliteľné premennou delitel
. Asi by nám ale stačilo zistiť, či existuje aspoň jeden deliteľ, ktorý je väčší ako 1 a menší ako cislo. Ak taký nájdeme (t.j. platí cislo % delitel == 0
), cyklus už ďalej nemusí preverovať zvyšné delitele. Predstavme si, že chceme zisťovať, či číslo 1000000 je prvočíslo. Tento náš program už pri treťom prechode cyklu (keď delitel
má hodnotu 3) „vie“, že pocet
je viac ako 2, a teda to určite nebude prvočíslo. Úplne zbytočne 999997-krát zisťuje, či nejaký deliteľ delí alebo nedelí naše cislo
. V tomto prípade by sa vykonávanie cyklu mohlo prerušiť a dostali by sme oveľa rýchlejšie rovnako správny výsledok. Na prerušovanie for-cyklu využijeme špeciálny príkaz break, ktorý ale môžeme použiť len v tele cyklu.
Vylepšime cyklus na zisťovanie prvočísel s prerušením pomocou break
:
cislo = int(input('Zadaj číslo: '))
pocet = 0
for delitel in range(1, cislo + 1):
if cislo % delitel == 0:
pocet += 1
if pocet > 2:
break
if pocet == 2:
print(cislo, 'je prvočíslo')
else:
print(cislo, 'nie je prvočíslo')
Otestujte zisťovaním, či je 1000000000
prvočíslo. Pomocou predchádzajúcej verzie programu (bez break
) by sme sa výsledku nedočkali, ale teraz sa dozvieme veľmi rýchlo, že:
Zadaj číslo: 1000000000
1000000000 nie je prvočíslo
Všimnite si, že v tomto prípade vôbec nepotrebujeme premennú pocet
, ktorá počítala počet deliteľov. Podľa hodnoty premennej delitel
po skončení cyklu, vieme presne povedať, či to bolo prvočíslo alebo nie. Upravme:
cislo = int(input('Zadaj číslo: '))
for delitel in range(2, cislo + 1):
if cislo % delitel == 0:
break
if cislo > 1 and delitel == cislo:
print(cislo, 'je prvočíslo')
else:
print(cislo, 'nie je prvočíslo')
Toto môžeme zapísať ešte krajšie pomocou pomocnej premennej prvocislo
, v ktorej si budeme pamätať, či sme pre dané číslo ešte nenašli žiadneho deliteľa (True
) alebo sme už jedného našli (False
), a teda to určite prvočíslo nebude:
cislo = int(input('Zadaj číslo: '))
prvocislo = True
for delitel in range(2, cislo):
if cislo % delitel == 0:
prvocislo = False
break
if prvocislo and cislo > 1:
print(cislo, 'je prvočíslo')
else:
print(cislo, 'nie je prvočíslo')
V tomto prípade ale for-cyklus nemôže ísť od 1
do cislo
, ale pôjde od 2
do cislo-1
.
Pripomeňme si ešte úlohu zo začiatku prednášky, v ktorej sa z počtu bodov zisťuje výsledné hodnotenie. Prepíšme teraz sériu príkazov if
do for-cyklu, v ktorom bude jediný if
a prerušenie cyklu pomocou break
:
body = int(input('Zadaj získaný počet bodov: '))
hranica = 150
for znamka in 'ABCDEF':
if body >= hranica:
break
hranica -= 10
if znamka != 'F':
print('za', body, 'bodov získavaš známku', znamka)
else:
print('za', body, 'bodov si nevyhovel a máš známku Fx')
V tomto riešení vidíme, že vo for-cykle sa postupne prechádzajú všetky hranice (hodnoty 150
, 140
, 130
, …) a pri prvej, ktorá vyhovuje (body >= hranica
), sa z cyklu vyskakuje. V tom prípade bude v premennej cyklu znamka
zodpovedajúce hodnotenie. Všimnite si, že v cykle pre známku F
vyjde hranica 100
. To znamená, že pre body
od 100
do 109
sa vyskočí z cyklu (pomocou break
) s hodnotou F
v premennej znamka
a pre body
menšie ako 100
cyklus normálne skončí a v premennej znamka
bude stále hodnota F
.
Podmienený cyklus¶
V Pythone existuje konštrukcia cyklu, ktorá opakuje vykonávanie postupnosti príkazov v závislosti od nejakej podmienky:
while podmienka: # opakuj príkazy, kým platí podmienka
prikaz
prikaz
...
Vidíme podobnosť s podmieneným príkazom if
- vetvením. Tento nový príkaz postupne:
zistí hodnotu podmienky, ktorá je zapísaná za slovom
while
ak má táto podmienka hodnotu
False
, blok príkazov, ktorý je telom cyklu, sa preskočí a pokračuje sa na nasledovnom príkaze za celým while-cyklom (podobne ako v príkazeif
bez vetvyelse
), hovoríme, že sa ukončilo vykonávanie cykluak má podmienka hodnotu
True
, vykonajú sa všetky príkazy v tele cyklu (v odsunutom bloku príkazov)a znovu sa testuje podmienka za slovom
while
, t.j. celé sa to opakuje
Najprv zapíšeme pomocou tohto cyklu, to čo už vieme pomocou for-cyklu:
for i in range(1, 21):
print(i, i * i)
Vypíše tabuľku druhých mocnín čísel od 1 do 20. Prepis na cyklus while
znamená, že zostavíme podmienku, ktorá bude testovať, napríklad premennú i
: tá nesmie byť väčšia ako 20. Samozrejme, že už pred prvou kontrolou premennej i
v podmienke cyklu while
, musí mať nejakú hodnotu:
i = 1
while i < 21:
print(i, i * i)
i += 1
V cykle sa vykoná print()
a zvýši sa hodnota premennej i
o jedna.
while
-cykly sa ale častejšie používajú vtedy, keď zápis pomocou for
-cyklu je príliš komplikovaný, alebo sa ani urobiť nedá.
Ukážeme to na programe, ktorý bude do jedného radu tesne vedľa seba kresliť stále sa zväčšujúce štvorce postupne so stranami 10, 20, 30, … Pritom bude dávať pozor, aby naposledy nakreslený štvorec „nevypadol“ z plochy - teda chceme skončiť skôr, ako by sme nakreslili štvorec, ktorý sa už celý nezmestí do grafickej plochy. Štvorce so stranou a
budeme kresliť takto:
canvas.create_rectangle(x, 200, x + a, 200 - a)
vďaka čomu, všetky ležia na jednej priamke (y = 200
). Keď teraz budeme posúvať x-ovú súradnicu vždy o veľkosť nakresleného štvorca, ďalší bude ležať tesne vedľa neho.
Program pomocou while-cyklu zapíšeme takto:
import tkinter
sirka = int(input('šírka plochy: '))
canvas = tkinter.Canvas(width=sirka)
canvas.pack()
x = 5
a = 10
while x + a < sirka:
canvas.create_rectangle(x, 200, x + a, 200 - a, fill='white')
x += a
a += 10
# príkazy za cyklom
tkinter.mainloop()
Program pracuje korektne pre rôzne šírky grafickej plochy. Ak zväčšovanie strany štvorca a += 10
nahradíme a = 2 * a
, program bude pracovať aj s takto zväčšovanými štvorcami (strany budú postupne 10, 20, 40, 80, …).
Zhrňme, ako funguje tento typ cyklu:
vyhodnotí sa podmienka
x + a < sirka
, t.j. pravý okraj štvorca, ktorý práve chceme nakresliť, sa ešte celý zmestí do grafickej plochyak je podmienka splnená (pravdivá), postupne sa vykonajú všetky príkazy, t.j. nakreslí sa ďalší štvorec so stranou
a
a potom sa posuniex
(ľavý okraj budúceho štvorca) o veľkosť práve nakresleného štvorcaa
a tiež sa ešte zmení veľkosť budúceho štvorcaa
o 10po vykonaní tela cyklu sa pokračuje v 1. kroku, t.j. opäť sa vyhodnotí podmienka
ak podmienka nie je splnená (nepravda), cyklus končí a ďalej sa pokračuje v príkazoch za cyklom
Uvedomte si, že podmienka nehovorí, kedy má cyklus skončiť, ale naopak - kým podmienka platí, vykonávajú sa všetky príkazy v tele cyklu. Teda, tento while-cyklus skončí vtedy, keď už podmienka platiť nebude, t.j. naplatí x + a < sirka
, čo je to isté ako x + a >= sirka
.
Pre šírku grafickej plochy 300 dostávame takýto obrázok:
Konkrétne tento program s while-cyklom vieme jednoducho prepísať pomocou for-cyklu a break
:
import tkinter
sirka = int(input('šírka plochy: '))
canvas = tkinter.Canvas(width=sirka)
canvas.pack()
x = 5
for a in range(10, sirka, 10):
if x + a >= sirka:
break
canvas.create_rectangle(x, 200, x + a, 200 - a, fill='white')
x += a
tkinter.mainloop()
Vyššie sme zostavili program, ktorý zisťoval, či je zadané číslo prvočíslo. Použili sme for-cyklus, v ktorom sme zadané číslo postupne delili všetkými číslami, ktoré sú menšie ako samotné číslo. Zistili sme, že na zisťovanie prvočísla nepotrebujeme skutočný počet deliteľov, ale malo by nám stačiť zistenie, či existuje aspoň jeden deliteľ. Keď sa vyskytne prvý deliteľ (t.j. platí cislo % delitel != 0
), cyklus môžeme ukončiť a vyhlásiť, že číslo nie je prvočíslo. Ak ani jedno číslo nie je deliteľom nášho čísla, hodnota premennej delitel
dosiahne cislo
a to je situácia, keď cyklus tiež skončí (t.j. keď delitel == cislo
, našli sme prvočíslo). Zapíšeme to while-cyklom:
cislo = int(input('Zadaj číslo: '))
delitel = 2
while delitel < cislo and cislo % delitel != 0:
delitel = delitel + 1
if delitel == cislo:
print(cislo, 'je prvočíslo')
else:
print(cislo, 'nie je prvočíslo')
Do podmienky while-cyklu sme pridali novú časť. Operátor and
tu má ten význam, že na to, aby sa cyklus opakoval, musia byť splnené obe časti. Uvedomte si, že cyklus skončí vtedy, keď prestane platiť zadaná podmienka, t.j. negácia podmienky (matematicky to upravíme):
not (delitel < cislo and cislo % delitel != 0)
not delitel < cislo or not cislo % delitel != 0
delitel >= cislo or cislo % delitel == 0
while-cyklus teda skončí vtedy, keď delitel >= cislo
, alebo cislo % delitel == 0
(teda, že deliteľ by bol už zbytočne veľký alebo našli sme hodnotu, ktorá delí naše číslo).
Zisťovanie druhej odmocniny¶
Ukážeme, ako zistíme druhú odmocninu čísla aj bez volania funkcie math.sqrt(x)
, resp. umocňovaním na jednu polovicu x**0.5
.
Prvé riešenie:
cislo = float(input('zadaj číslo:'))
x = 0
while x ** 2 < cislo:
x += 1
print('odmocnina', cislo, 'je', x)
Takto nájdené riešenie môže byť dosť nepresné, lebo x
zvyšujeme o 1, takže, napríklad odmocninu z 26
vypočíta ako 6
. Skúsme zjemniť krok, o ktorý sa mení hľadané x
:
cislo = float(input('zadaj číslo:'))
x = 0
while x ** 2 < cislo:
x += 0.001
print('odmocnina', cislo, 'je', x)
Teraz dáva program lepšie výsledky, ale pre väčšiu zadanú hodnotu mu to trvá citeľne dlhšie - skúste zadať, napríklad 10000000
. Keďže mu vyšiel výsledok približne 3162.278
a dopracoval sa k nemu postupným pripočítavaním čísla 0.001
k štartovému 1, musel urobiť vyše 3 miliónov pripočítaní a tiež toľkokrát testov vo while-cykle (podmienky x**2 < cislo
). Pre toto je takýto algoritmus nepoužiteľne pomalý.
Využijeme inú ideu:
zvolíme si interval, v ktorom sa určite bude nachádzať hľadaný výsledok (hľadaná odmocnina), napríklad nech je to interval
<1, cislo>
(pre čísla väčšie ako 1 je aj odmocnina väčšia ako 1 a určite je menšia ako samotnecislo
)ako
x
(prvý odhad našej hľadanej odmocniny) zvolíme stred tohto intervaluzistíme, či je druhá mocnina tohto
x
väčšia ako zadanécislo
alebo menšiaak je väčšia (
x
je už zbytočne veľké), tak upravíme predpokladaný interval, tak že jeho hornú hranicu zmeníme nax
ak je ale menšia, upravíme dolnú hranicu intervalu na
x
tým sa nám interval zmenšil na polovicu
toto celé opakujeme, kým už nie je nájdené
x
dostatočne blízko k hľadanému výsledku, t.j. či sa nelíši od výsledku menej ako zvolený rozdiel (epsilon)
Zapíšme to:
cislo = float(input('zadaj číslo:'))
od = 0
do = cislo
x = (od + do) / 2
pocet = 0
while abs(x ** 2 - cislo) > 0.001:
if x ** 2 > cislo:
do = x
else:
od = x
x = (od + do) / 2
pocet += 1
print('druhá odmocnina', cislo, 'je', x)
print('počet prechodov while-cyklom bol', pocet)
Ak spustíme program pre 10000000
dostávame:
zadaj číslo:10000000
druhá odmocnina 10000000 je 3162.2776600670477
počet prechodov while-cyklom bol 41
čo je výrazné zlepšenie oproti predchádzajúcemu riešeniu, keď prechodov while-cyklom (hoci jednoduchších) bolo vyše 3 milióny.
Nekonečný cyklus¶
Cyklus s podmienkou, ktorá má stále hodnotu True
, bude nekonečný. Napríklad:
i = 0
while i < 10:
i -= 1
Nikdy neskončí, lebo premenná i
bude stále menšia ako 10. Takéto výpočty môžeme prerušiť stlačením klávesov Ctrl/C.
Aj nasledovný cyklus je úmyselne nekonečný:
while 1:
pass
Pripomíname, že príkaz pass
je prázdny príkaz, ktorý nerobí nič. V tomto príklade pass
označuje prázdne telo cyklu, a teda tento cyklus bude nikdy nekončiaci.
Už vieme, že príkaz break
môžeme použiť v tele for-cyklu a vtedy sa zvyšok cyklu nevykoná. Nasledovný príklad ilustruje použitie break
aj vo while-cykle:
sucet = 0
while True:
retazec = input('zadaj číslo: ')
if retazec == '':
break
sucet += int(retazec)
print('súčet prečítaných čísel =', sucet)
V tomto príklade sa čítajú čísla zo vstupu, kým nezadáme prázdny reťazec: vtedy cyklus končí a program vypíše súčet prečítaných čísel. Napríklad:
zadaj číslo: 5
zadaj číslo: 17
zadaj číslo: 2
zadaj číslo:
súčet prečítaných čísel = 24
Cvičenia¶
Napíš program, ktorý zadané číslo rozloží na prvočinitele (vyjadrí ho ako súčin prvočísel). Tento rozklad zapíš v tvare rovnosti s násobením:
zadaj číslo: 60 60 = 2 * 2 * 3 * 5
zadaj číslo: 1001 1001 = 7 * 11 * 13
zadaj číslo: 37 37 = 37
V programe bude while-cyklus, v ktorom budeš postupne skúšať deliť zadané číslo rôznymi deliteľmi: ak je číslo týmto deliteľom deliteľné, tak ho vypíšeš a samotné číslo vydelíš týmto deliteľom, inak deliteľ zvýšiš o 1 a celé sa to opakuje.
Napíš program, ktorý vypíše cifry zadaného čísla postupným delením desiatimi, teda vo while-cykle vypíšeš poslednú cifru (číslo % 10) a pritom ešte samotné číslo vydelíš 10. Súčasne každú túto cifru pripočítaš do počítadla
cs
(ciferný súčet). Môžeš dostať takýto výstup:zadaj číslo: 4132 2 3 1 4 ciferný súčet = 10
Všimni si, že cifry sú vypísané v opačnom poradí ako sú v zadanom čísle.
Teraz vypíš cifry do grafickej plochy (zrejme sprava do ľava). Program jednotlivé cifry vypíše do farebných štvorcov. Napríklad, pre číslo
510726
by si mal dostať:
Uprav predchádzajúci program tak, aby sa číslo vypísalo v osmičkovej sústave (zrejme namiesto delenia
10
budeš deliť8
). Pre číslo1753
by si mal dostať:Tento výsledok môžeš porovnať s volaním štandardnej funkcie
oct
(resp. pomocou formátovacej šablóny):>>> oct(1753) '0o3331' >>> f'{1753:o}' '3331'
Vyskúšaj upraviť tento program tak, aby vypisoval cifry v dvojkovej sústave. Aj tu môžeš pre kontrolu využiť štandardnú funkciu
bin
(resp. formátovaciu šablónu):>>> bin(479) '0b111011111' >>> f'{479:b}' '111011111'
Nasledovný program vykresľuje farebné krúžky v štvorcovej sieti
n
xn
a zafarbuje ich podľa podmienky v príkazeif
:import tkinter canvas = tkinter.Canvas() canvas.pack() n = 13 for i in range(n): for j in range(n): x = j * 20 + 100 y = i * 20 + 12 if i == 5: farba = 'red' else: farba = 'white' canvas.create_oval(x - 8, y - 8, x + 8, y + 8, fill=farba) tkinter.mainloop()
A. Zmeň túto podmienku tak, aby sa nakreslil obrázok, v ktorom sa zafarbí stredný rad a stredný stĺpec (v programe nemeň iné príkazy, nepridávaj ďalšie):
B. Zmeň túto podmienku tak, aby sa nakreslil obrázok, v ktorom sa zafarbia obe uhlopriečky:
Programy by mali fungovať správne aj pri zmenenom rozmere
n
. Vyskúšaj napríkladn=10
.
Budem hrať takúto hru: kladiem vedľa seba do radu mince s náhodnými hodnotami z
<1, 4>
; skončím, keď ich súčet bude väčší alebo rovný danémuk
. Ak skončil so súčtom, ktorý je rovnýk
, vypíše text'HURÁ'
inak'ŠKODA'
. Napíš program, ktorý v grafickom režime túto hru odsimuluje 10-krát a zakreslí to pod seba, napríklad pre ˙˙k=21˙˙ môžeš dostať takýto obrázok:
Využi program z prednášky, v ktorom sa vykresľovalo 1000 farebných bodiek (malé krúžky s polomerom 5 bez obrysu) podľa toho či mali
x
-ovú, aleboy
-ovú súradnicu menšiu alebo väčšiu ako150
. Veľkosť grafickej plochy bola 300x300. Zmeň v tomto programe sériu príkazovif
tak, aby kreslené bodky vytvorili uhlopriečky štvorca. Asi tu budeš vedieť využiť podmienkyx < y
alebo300 - x < y
:
V ďalšej verzii bodkovacej úlohy vybodkuješ kruh. Program opäť nakreslí 4000 náhodných bodiek, ale tie z nich, ktoré majú vzdialenosť od
(x0, y0)
menšiu akor
, zafarbí na červeno, zvyšné na modro. Napríklad pre premenné:x0, y0 = 180, 130 r = 110
by si mal dostať takýto obrázok:
Vzdialenosť dvoch bodov v rovine môžeš počítať podľa vzorca
sqrt((x1-x2)**2 + (y1-y2)**2)
.Aj v ďalšej verzii tohto programu budeš generuovať náhodné bodky v
300
x300
. Bodiek teraz vygeneruj aspoň10000
a zmenši ich na polomer2
. Červené bodky budú tie, pre ktoré platíx*x+y*y<=300*300
, zvyšné budú modré. Program ma záver vypíše podiel počtu červených ku všetkým bodkám krát4
. Zamysli sa nad tým, prečo sa tento podiel blíži k číslu pi. Napríklad:vyšiel tento podiel
3.1436
.
Napíš program, ktorý v grafickej ploche najprv vygeneruje
n
náhodných bodiek (malé kruhy). Potom nakreslí čo najmenší obdĺžnik tak, aby sa v ňom nachádzali všetky nakreslené body. Napríklad, pren=7
môžeš dostať takýto obrázok:
Vlajku Nemecka môžeš do štandardnej veľkosti grafickej plochy nakresliť tak, že pomocou cyklu vygeneruješ 10000 náhodných súradníc
x
z intervalu<10, 350>
ay
z intervalu<10, 250>
. Na vygenerované súradnice nakreslíš farebnú bodku (kruh s polomerom5
bez obrysu). Farbu bodky zvolíš podľa y-ovej súradnice:Teraz vytvor podobný program, ktorý nakreslí vlajku Českej republiky:
Dvojkový logaritmus môžeme počítať pomocou funkcie
math.log2(cislo)
, ale vieme na to použiť aj algoritmus z prednášky, ktorý delením intervalu na polovice počítal druhú odmocninu. Napíš program, ktorý to s nejakou presnosťou vypočíta takýmto algoritmom. Napríklad po spustení:zadaj číslo: 1000 logaritmus 1000.0 je 9.965784382075071 >>> import math >>> math.log2(1000) 9.965784284662087
Pomocou polygónu nakresli farebný trojuholník zadaný tromi jeho vrcholmi
a
,b
,c
. Potom do neho nakresli vpísaný trojuholník (jeho vrcholy sú v stredoch strán). Toto opakuj, kým sú všetky dĺžky strán trojuholníka aspoň 30. Trojuholníky vyfarbuj náhodnými farbami. Napríklad pre:a = 10, 100 b = 250, 10 c = 300, 250
po spustení:
Pomôcka: ak mám dva vrcholy
(x1, y1)
a(x2, y2)
, potom stred úsečky jex3, y3 = (x1+x2)/2, (y1+y2)/2
2. Týždenný projekt¶
Napíš pythonovský skript, ktorý najprv pomocou troch volaní príkazu input('?')
postupne prečíta:
jedno celé číslo
n = int(input('?'))
dve celé čísla (oddelene medzerou):
sirka
avyska
tri mená farieb (oddelene medzerou)
Potom do grafickej plochy nakreslí pyramídu zloženú z farebných obdĺžnikov veľksti (sirka, vyska)
(pomocou``create_rectangle()``). Pyramída sa skladá z n
poschodí: na vrchu pyramídy je jeden obdĺžnik, pod ním sú dva odsunuté vľavo o polovicu šírky, pod nimi sú tri opäť odsunuté vľavo. Tieto obdĺžniky budú zafarbené tromi farbami tak, že žiadne dva, ktoré sa dotýkajú, nemajú rovnakú farbu. Tieto tri farby obdĺžnikov sú zadefinované v treťom vstupe (input
).
Napríklad, pre takýto vstup:
?4
?50 20
?green blue maroon
môžeš dostať takýto obrázok:
V tvojom riešení nepoužívaj žiaden iný modul okrem tkinter
. Z Pythonu používaj len príkazy z prvých štyroch prednášok. Do grafického plátna (canvas
) kresli len pomocou create_rectangle
, do ktorého pošleš 4 čísla ako súradnice dvoch bodov a pomenovaný parameter fill
. Je jedno, kde v plátne umiestniš nakreslenú pyramídu, môžeš predpokladať, že testovač má neobmedzené rozmery plochy.
Za tento projekt môžeš získať maximálne 3 body, pričom,
ak sú obdĺžniky umiestnené správne, ale nie je v poriadku niečo s farbami, tak iba 1 bod
ak sú obdĺžniky uniestnené správne a žiadne dva susediace nemajú rovnakú farbu, tak 2 body
ak vykresľuješ všetky obdĺžniky v takom poradí, že najprv sú všetky jednej farby, až potom všetky obdĺžniky druhej farby a na záver všetky obdĺžniky tretej farby, tak za správne riešenie dostaneš 3 body (je jedno v akom poradí farieb).
Tvoj odovzdaný program s menom riesenie.py
musí začínať tromi riadkami komentárov (zmeň meno a dátum odovzdania):
# 2. zadanie: pyramida
# autor: Janko Hraško
# datum: 3.10.2024
Súbor riesenie.py
odovzdávaj na úlohový server https://list.fmph.uniba.sk/, kde ho môžeš nechať otestovať ľubovoľný počet krát.