4. Podmienky

Podmienený príkaz

Pri programovaní často riešime situácie, keď sa program má 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, porovná túto hodnotu s požadovanou hranicou, napríklad 50 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 >= 50:
    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ň 90 bodov

  • známka B aspoň 80 bodov

  • známka C aspoň 70 bodov

  • známka D aspoň 60 bodov

  • známka E aspoň 50 bodov

  • známka Fx menej ako 50 bodov

Podmienka pre získanie známky A:

if body >= 90:
    print('za', body, 'bodov získavaš známku A')
else:
    ...

Ak je bodov menej ako 90, už to môže byť len horšia známka: dopíšeme testovanie aj známky B:

if body >= 90:
    print('za', body, 'bodov získavaš známku A')
else:
    if body >= 80:
        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 >= 90:
    print('za', body, 'bodov získavaš známku A')
else:
    if body >= 80:
        print('za', body, 'bodov získavaš známku B')
    else:
        if body >= 70:
            print('za', body, 'bodov získavaš známku C')
        else:
            if body >= 60:
                print('za', body, 'bodov získavaš známku D')
            else:
                if body >= 50:
                    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 >= 90:
    print('za', body, 'bodov získavaš známku A')
elif body >= 80:
    print('za', body, 'bodov získavaš známku B')
elif body >= 70:
    print('za', body, 'bodov získavaš známku C')
elif body >= 60:
    print('za', body, 'bodov získavaš známku D')
elif body >= 50:
    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 >= 90:
    print('za', body, 'bodov získavaš známku A')
if 80 <= body < 90:
    print('za', body, 'bodov získavaš známku B')
if 70 <= body < 80:
    print('za', body, 'bodov získavaš známku C')
if 60 <= body < 70:
    print('za', body, 'bodov získavaš známku D')
if 50 <= body < 60:
    print('za', body, 'bodov získavaš známku E')
if body < 50:
    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 80 <= body < 90 sme mohli zapísať aj takto 90 > body >= 80)

V Pythone môžeme zapisovať podmienky podobne, ako je to bežné v matematike:

body < 90

je menšie ako

body <= 50

je menšie alebo rovné

body == 50

rovná sa

body != 77

nerovná sa

body > 100

je väčšie ako

body >= 90

je väčšie alebo rovné

40 < body <= 50

je väčšie ako … a zároveň menšie alebo rovné …

a < b < c

a je menšie ako b a zároveň je b menšie ako c

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ť bude 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
from random import randint

canvas = tkinter.Canvas()
canvas.pack()

for i in range(100):
    x = randint(1, 350)
    y = randint(1, 230)
    a = 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)

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

_images/04_01.png

Pokračujme s grafickým programom, ktorý na náhodné pozície nakreslí 1000 malých krúžkov:

import tkinter
from random import randint

canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()

for i in range(1000):
    x = randint(1, 300)
    y = randint(1, 300)
    farba = 'blue'
    canvas.create_oval(x-5, y-5, x+5, y+5, fill=farba, width=0)
_images/04_02.png

Teraz 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
from random import randint

canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()

for i in range(1000):
    x = randint(1, 300)
    y = 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)
_images/04_03.png

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
from random import randint

canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()

for i in range(1000):
    x = randint(1, 300)
    y = 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)
_images/04_04.png

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
from random import randint

canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()

for i in range(1000):
    x = randint(1, 300)
    y = 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)
_images/04_05.png

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 podmienky

  • podmienka1 or podmienka2 … (alebo) znamená, že musí platiť aspoň jedna z podmienok

  • not 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
from random import randrint

canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()

for i in range(1000):
    x = randint(1, 300)
    y = 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)
_images/04_06.png

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.

Logický súčin a súčasne

A

B

A and B

False

False

False

True

False

False

False

True

False

True

True

True

Logický súčet alebo

A

B

A or B

False

False

False

True

False

True

False

True

True

True

True

True

Negácia neplatí

A

not A

False

True

True

False

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:

False z iných typov

typ

False

True

int

x == 0

x != 0

float

x == 0.0

x != 0.0

str

x == ''

x != ''

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:

operácia: prvý and druhý

  • ak prvý nie je False, tak

    • výsledkom je druhý

  • inak (teda prvý je False)

    • výsledkom je prvý

Môžeme upraviť aj tabuľku pravdivostných hodnôt:

Logický súčin a súčasne

A

B

A and B

False

hocičo

A

True

hocičo

B

operácia: prvý or druhý

  • ak prvý nie je False, tak

    • výsledkom je prvý

  • inak (teda prvý je False)

    • výsledkom je druhý

Tabuľka:

Logický súčet alebo

A

B

A or B

False

hocičo

B

True

hocičo

A

operácia: not prvý

  • ak prvý nie je False, tak

    • výsledkom je False

  • inak

    • výsledkom je True

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
from random import randint, randrange

canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()

for i in range(10):
    x = randint(1, 300)
    y = randint(1, 300)
    a = randint(5, 50)

    if randrange(2):               # t.j. 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)
_images/04_07.png

Približne rovnaké výsledky by sme dostali, ak by sme hádzali kockou so 6 možnosťami (random.randrange(1, 7)) 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
from random import tandint, randrange

canvas = tkinter.Canvas(bg='white', width=300, height=300)
canvas.pack()

for i in range(1000):
    x = randint(1, 300)
    y = randint(1, 300)
    if randrange(50):             # t.j. randrange(50) != 0
        farba = 'red'
    else:
        farba = 'blue'
    canvas.create_rectangle(x-5, y-5, x+5, y+5, fill=farba, width=0)
_images/04_08.png

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.

Prerušenie cyklu pomocou break

Príkaz break v tele cyklu spôsobí ukončenie cyklu, t.j. vykonávanie príkazov cyklu sa v tomto momente preruší a pokračuje sa až za prvým príkazom za cyklom. Premenná cyklu bude mať potom hodnotu naposledy vykonávaného prechodu. Príkaz break sa musí použiť v nejakom vnorenom podmienenom príkaze if. Napríklad:

for premenna in ...:
    príkazy1
    if podmienka:
        break
    príkazy2
príkazy_za_cyklom

V každom prechode cyklu sa najprv vykonajú príkazy1. Potom odkontroluje podmienka: ak je pravdivá, vykonávanie cyklu končí a pokračuje sa na príkazy_za_cyklom. Ak je podmienka nepravdivá (neplatí), cyklus pokračuje vykonávaním príkazy2 a prípadnými ďalšími prechodmi 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.


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íkaze if bez vetvy else), hovoríme, že sa ukončilo vykonávanie cyklu

  • ak 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

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:

  1. vyhodnotí sa podmienka x + a < sirka, t.j. pravý okraj štvorca, ktorý práve chceme nakresliť, sa ešte celý zmestí do grafickej plochy

  2. ak je podmienka splnená (pravdivá), postupne sa vykonajú všetky príkazy, t.j. nakreslí sa ďalší štvorec so stranou a a potom sa posunie x (ľavý okraj budúceho štvorca) o veľkosť práve nakresleného štvorca a a tiež sa ešte zmení veľkosť budúceho štvorca a o 10

  3. po vykonaní tela cyklu sa pokračuje v 1. kroku, t.j. opäť sa vyhodnotí podmienka

  4. 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.

Pre šírku grafickej plochy 300 dostávame takýto obrázok:

_images/04_09.png

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

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 = 1
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 = 1
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 samotne cislo)

  • ako x (prvý odhad našej hľadanej odmocniny) zvolíme stred tohto intervalu

  • zistíme, či je druhá mocnina tohto x väčšia ako zadané cislo alebo menšia

  • ak je väčšia (x je už zbytočne veľké), tak upravíme predpokladaný interval, tak že jeho hornú hranicu zmeníme na x

  • 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 = 1
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.0 je 3162.2776600480274
počet prechodov while-cyklom bol 44

č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

L.I.S.T.


  1. Prednáška začína riešením úlohy, v ktorej sa z počtu bodov zistí známka. Tento program sériou príkazov if-elif-else zisťuje interval, v ktorom sa nachádzajú zadané body. Napíš program, ktorý túto istú úlohu rieši pomocou for-cyklu:

    body = int(input('Zadaj získaný počet bodov: '))
    hranica = 90
    for znamka in 'ABCDEF':
        ...
    

    V tele cyklu sa bude nachádzať príkaz if s vyskočením break.


  1. Napíš program, ktorý zadané číslo rozloží na prvočinitele (vyjadrí ho ako súčin prvočísel). Napríklad po spustení:

    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 deliteľom deliteľné, tak ho vypíš a číslo vydeľ, inak deliteľ zvýš o 1.


  1. 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 potom 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í.


  1. Využi while-cyklus z predchádzajúcej úlohy a vypíš cifry do grafickej plochy. Program jednotlivé cifry vypíše do farebných štvorcov. Napríklad pre číslo 510726 by si mal dostať:

    _images/04_c01.png

  1. Uprav predchádzajúci program tak, aby sa číslo vypísalo v osmičkovej (resp. dvojkovej) sústave. Pre číslo 1753 by si mal dostať:

    _images/04_c02.png

    Tento výsledok môžeš porovnať s volaním štandardnej funkcie oct:

    >>> oct(1753)
    '0o3331'
    

    alebo bin (pre dvojkovú sústavu):

    >>> bin(479)
    '0b111011111'
    

  1. Nasledovný program vykresľuje farebné krúžky v štvorcovej sieti n x n a zafarbuje ich podľa podmienky v príkaze if:

    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 + 10
            if i == 5:
                farba = 'red'
            else:
                farba = 'white'
            canvas.create_oval(x-8, y-8, x+8, y+8, fill=farba)
    

    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):

    _images/04_c03.png

    B. Zmeň túto podmienku tak, aby sa nakreslil obrázok, v ktorom sa zafarbí obe uhlopriečky:

    _images/04_c04.png

    Programy by mali fungovať správne aj pri zmenenom rozmere n. Vyskúšaj napríklad n=10.


  1. Žiaci sú v rade zoradení podľa veľkosti (od najmenšieho). Napíš program, ktorému najprv postupne oznamujeme (pomocou input) výšky žiakov a ten na záver (zadali sme „prázdnu“ výšku) vypíše, či boli zoradení správne. Použi príkaz while (dopredu nepoznáme počet žiakov). Mali by fungovať takéto spustenia programu:

    zadávaj výšky žiakov
       výška 1. žiaka: 100
       výška 2. žiaka: 110
       výška 3. žiaka: 110
       výška 4. žiaka: 120
       výška 5. žiaka:
    všetci žiaci sú zoradení správne
    
    zadávaj výšky žiakov
       výška 1. žiaka: 100
       výška 2. žiaka: 120
       výška 3. žiaka: 110
       výška 4. žiaka: 140
       výška 5. žiaka: 145
       výška 6. žiaka:
    žiaci nie sú správne zoradení
    
    zadávaj výšky žiakov
       výška 1. žiaka: 180
       výška 2. žiaka:
    všetci žiaci sú zoradení správne
    

  1. Napíš program, ktorý nakreslí gramofónovú LP platňu ako niekoľko sústredných kružníc. Najväčšia z nich má polomer r a každá ďalšia je o 3 menšia. Najmenšia kružnica by nemala mať menší polomer ako 20. Každú k-tu kružnicu nakresli šedou farbou (začni od najväčšej). Napríklad pre premenné:

    x, y = 190, 130
    r = 120
    k = 6
    

    by si mal dostať takýto obrázok:

    _images/04_c05.png

  1. 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ému k. Ak skončil so súčtom, ktorý je rovný k, vypíše text 'HURÁ' inak 'ŠKODA'. Napíš program, ktorý túto hru odsimuluje 10-krát a zakreslí to pod seba, napríklad pre ˙˙k=21˙˙ môžeš dostať takýto obrázok:

    _images/04_c06.png

  1. Napíš program, ktorý nakreslí 2000 náhodných bodiek (farebných kruhov s polomerom 5 bez obrysu) a tie z nich, ktoré majú vzdialenosť od (x0, y0) menšiu ako r zafarbí na červeno, zvyšné na žlto. Napríklad pre premenné:

    x0, y0 = 190, 130
    r = 90
    

    by si mal dostať takýto obrázok:

    _images/04_c07.png

    Vzdialenosť dvoch bodov v rovine môžeš počítať podľa vzorca sqrt((x1-x2)**2 + (y1-y2)**2).


  1. Napíš program, ktorý najprv v grafickej ploche 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 pre n=7 môžeš dostať takýto obrázok:

    _images/04_c08.png

  1. Hrací automat mi pri každom zatlačení (hodil som do neho 1 euro) dá 3 náhodné čísla z <1, 20>. Ak sú medzi týmito tromi číslami dve rovnaké, vyhrávam 5 euro, ak sú všetky tri rovnaké vyhrávam 100 euro. Začal som s nejakou sumou a hral som maximálne 1000 zatlačení, prípadne som skončil skôr, keď som všetko prehral. Napíš program, ktorý to všetko odsimuluje: pri každom zatlačení vypíše '+5' alebo '+100', ak som niečo vyhral, alebo '-1', ak som nič nevyhral. Napríklad:

    začínam so sumou: 20
    štart -1-1-1-1-1-1-1+5-1-1-1-1+5-1-1-1-1-1-1-1-1+5-1+5-1-1-1-1-1-1+5-1+5
    -1-1-1-1-1-1-1-1-1+5+5-1-1-1-1+5-1-1-1+5-1-1-1+5+5-1-1+5+5-1-1+5-1-1-1-1
    -1-1-1-1+5-1-1-1+5-1+5-1-1-1-1+5-1+5-1-1+5-1-1-1-1-1-1-1+5-1+5-1-1-1-1-1
    +5-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1+5-1-1-1-1-1+5-1
    -1-1-1+5-1-1+5-1-1-1-1-1-1-1-1-1-1-1-1
    zostalo mi 0 euro
    

  1. 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 z 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
    

  1. Vlajku Nemecka môžeš nakresliť tak, že pomocou cyklu vygeneruješ 10000 náhodných súradníc x z intervalu <10, 350> a y z intervalu <10, 250>. Na vygenerované súradnice nakreslíš farebný kruh s polomerom 5 (bez obrysu). Farbu kruhu zvolíš podľa y-ovej súradnice:

    • ak je y < 90 kresli čierny krúžok,

    • inak, ak je y < 170 kresli čierny krúžok,

    • inak nakresli žltý krúžok.

    Mal by si dostať takýto obrázok:

    _images/04_c09.png

  1. Vytvor program, ktorý podobne, ako v predchádzajúcej úlohe, nakreslí vlajku bývalého Československa:

    _images/04_c10.png

  1. Napíš program, ktorý generuje náhodné rozmery kvádrov (farebný obdĺžnik s náhodnými stranami z <10, 70>) a vykresľuje ich tesne vedľa seba. Takéto generovanie kvádrov skončí vtedy, ak by mal ďalší kváder presiahnuť šírku grafickej plochy (zadanú v premennej sirka). Napríklad pre sirka=400 môžeš dostať:

    _images/04_c11.png

    Teraz pozmeň tento program tak, aby najprv každý vygenerovaný kváder otočil tak, aby jeho kratšia hrana bola na spodu a väčšia zvislo. Môžeš dostať takýto obrázok:

    _images/04_c12.png

  1. Napíš program, ktorý nakreslí náhodného snehuliaka takto: najprv vygeneruje tri náhodné polomery r1, r2, r3 z intervalu `<10, 50>. Potom nakreslí samotného snehuliaka tak, že prvý kruh má x, y = 190, 20+r1, každý ďalší má posunutú y súradnicu. Môžeš dostať takýto obrázok:

    _images/04_c13.png

    Teraz pozmeň tento program tak, aby sa ešte pred kreslením preusporadali polomery: r1 <= r2 <= r3. Môžeš dostať takýto obrázok:

    _images/04_c14.png

  1. Napíš program, ktorý nakreslí náhrdelník z n korálikov. Tieto budú rovnomerne rozložené na kružnici s polomerom r. Každý k-ty korálik je zafarbený farba2 inak farba1. Napríklad pre premenné:

    x0, y0 = 190, 130          # stred kružnice
    r = 100                    # polomer kružnice
    farba1, farba2 = 'red', 'yellow'
    n, k = 35, 7               # počet korálikov a konštanta k
    

    môžeš dostať takýto obrázok:

    _images/04_c15.png

    Veľkosť korálikov (malých farebných kruhov) vypočítaj podľa veľkosti r a počtu korálikov n tak, aby sa navzájom dotýkali a ich stredy boli na myslenej kružnici. Napríklad pre n, k = 15, 3 môžeš dostať:

    _images/04_c16.png