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. 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. 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 plati, vykonaj 1. skupinu prikazov
    prikaz
    prikaz
    ...
else:            # ak podmienka neplati, vykonaj 2. skupinu prikazov
    prikaz
    prikaz
    ...

V našom 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. 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 plati, vykonaj 1. skupinu prikazov
    prikaz
    ...
elif podmienka_2:    # ak podmienka_1 neplati, ale plati podmienka_2, ...
    prikaz
    ...
elif podmienka_3:    # ak ani podmienka_1 ani podmienka_2 neplatia, ale plati podmienka_3, ...
    prikaz
    ...
else:                # ak ziadna z podmienok neplati, ...
    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 plati, vykonaj skupinu prikazov
    prikaz
    prikaz
    ...              # ak podmienka neplati, nevykonaj nic

Mohli by sme to zapísať aj takto:

if podmienka:        # ak podmienka plati, vykonaj skupinu prikazov
    prikaz
    prikaz
    ...
else:
    pass             # ak podmienka neplati, nevykonaj nic

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 randrange

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

for i in range(1000):
    x = randrange(300)
    y = randrange(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 randrange

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

for i in range(1000):
    x = randrange(300)
    y = randrange(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 randrange

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

for i in range(1000):
    x = randrange(300)
    y = randrange(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 randrange

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

for i in range(1000):
    x = randrange(300)
    y = randrange(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. 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 iste ako  a != b
True
>>> 1 == '1'
False
>>> 1 < '2'             # nemozeme takto porovnavat cisla 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 randrange

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

for i in range(1000):
    x = randrange(300)
    y = randrange(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. 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.

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

>>> 1 + 2 and 3 + 4       # kedze 1+2 nie je False, vysledkom je 3+4
7
>>> 'ahoj' or 'Python'    # kedze 'ahoj' nie je False, vysledkom je 'ahoj'
'ahoj'
>>> '' or 'Python'        # kedze '' je False, vysledkom je 'Python'
'Python'
>>> 3 < 4 and 'kuk'       # kedze 3<4 nie je False, vysledkom je 'kuk'
'kuk'

Podmienený príkaz sa často používa pri náhodnom rozhodovaní. Napr. 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 randrange

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

for i in range(10):
    x = randrange(300)
    y = randrange(300)
    a = randrange(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 randrange

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

for i in range(1000):
    x = randrange(300)
    y = randrange(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 zapisat 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.

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  6, 28, 496, 8128,
=== viac ich  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.

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 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 prikazy, kym plati 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), 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. 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 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(bg='white', width=sirka)
canvas.pack()

x = 5
a = 10
while x + a < sirka:
    canvas.create_rectangle(x, 200, x + a, 200 - a)
    x = x + a
    a = a + 10
# príkazy za cyklom

Program pracuje korektne pre rôzne šírky grafickej plochy. Ak zväčšovanie strany štvorca a = 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ť na for-cyklus:

import tkinter

sirka = int(input('šírka plochy: '))

canvas = tkinter.Canvas(bg='white', 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)
    x = 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 označuje to, že aby sa cyklus opakoval, musia byť splnené obe časti. Uvedomte si, že cyklus skončí vtedy, keď prestane platiť zadaná podmienka, t.j. (a ďalej to matematicky 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 cislo:'))

x = 1
while x ** 2 < cislo:
    x = 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. odmocninu z 26 vypočíta ako 6. Skúsme zjemniť krok, o ktorý sa mení hľadané x:

cislo = float(input('zadaj cislo:'))

x = 1
while x ** 2 < cislo:
    x = x + 0.001

print('odmocnina', cislo, 'je', x)

Teraz dáva program lepšie výsledky, ale pre väčšiu zadanú hodnotu mu to citeľne dlhšie trvá - skúste zadať napr. 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. 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, 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 cislo:'))

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 cislo: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ónov.

Nekonečný cyklus

Cyklus s podmienkou, ktorá má stále hodnotu True, bude nekonečný. Napr.

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.

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. Športovec prvý deň prebehol x kilometrov. Každý ďalší deň prebehol o 10% viac ako v predchádzajúci deň. Napíš program, ktorý pre dané y zistí, v ktorý deň športovec prebehne aspoň y kilometrov.

    • napr. po spustení

      zadaj km pre prvý deň: 10
      zadaj cieľové km: 20
      na 9. deň prebehne aspoň 20 km
      
      zadaj km pre prvý deň: 100
      zadaj cieľové km: 121
      na 3. deň prebehne aspoň 121 km
      
  2. Budeme konštruovať takúto postupnosť celých čísel:

    • začneme zadaným číslom n

    • ak je párne, vydelíme ho 2

    • inak sa vynásobí 3 a pripočíta 1

    • toto sa opakuje, kým nedostaneme číslo 1

    • Napíš program, ktorý pre dané štartové číslo vypíše takto skonštruovanú postupnosť.

    • napr.

      zadaj číslo: 44
      44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1
      
  3. Napíš program, ktorý číta desatinné čísla zo vstupu a keď zadáme 0, čítanie čísel zo vstupu skončí a program vypíše súčet všetkých týchto čísel. Použi while-cyklus.

    • napr. po spustení:

      zadaj 1. číslo: 17
      zadaj 2. číslo: 3.14
      zadaj 3. číslo: -9.8
      zadaj 4. číslo: 6
      zadaj 5. číslo: 0
      súčet všetkých prečítaných čísel je 16.34
      
  4. Napíš program, ktorý nakreslí gramofónovú platňu: sú to sústredné kružnice s polomermi 5, 8, 11, 14, …, 119 a s hrúbkou pera 2 (pomenovaný parameter width).

    • napr.

      _images/04_10.png
  5. Do predchádzajúceho programu s gramofónovou platňou pridaj zafarbenie niektorých kružníc (pomenovaný parameter outline) takto:

    • kružnice, ktorých polomer je deliteľný číslom 7 (napr. polomer 14), budú modré

    • inak, ktorých polomer je deliteľný číslom 5 (napr. 5 alebo 20), budú červené

    • inak budú čierne

    • napr.

      _images/04_11.png
  6. Predchádzajúci program pozmeň takto:

    • namiesto kružníc s hrúbkou pera 2 kresli farebné kruhy bez obrysu (pomenovaný parameter width=0)

    • nastavovaná farba obrysu sa stane farbou výplne (modrá a červená)

    • namiesto čiernej farby namiešaj náhodnú zelenú farbu, t.j. farbu v tvare '#00xx00', kde xx je dvojciferné šestnástkové náhodné číslo v rozsahu <100, 255>

    • dostaneš napr. takýto obrázok:

      _images/04_12.png
  7. Postupným delením daného čísla desiatimi nám zvyšky po delení dávajú cifry tohto čísla. Tieto cifry dostávame v opačnom poradí, ako sú v desiatkovom zápise. Napr. číslo 374 dáva (po delení 10) zvyšok 4 a samotné číslo po delení 10 stratí túto poslednú desiatkovú cifru - dostávame číslo 37. Keď to zopakujeme, zvyšok je 7 a číslo sa zmení na 3. Posledným delením dostávame cifru 3 a číslo sa zmení na 0. Takto sme postupne dostali cifry 4, 7, 3. Napíš program, ktorý prečíta nejaké celé číslo a jeho cifry vypíše vedľa seba v prevrátenom poradí. Použi while-cyklus.

    • napr. po spustení

      zadaj číslo: 523994701
      prevrátené poradie cifier pre 523994701 je 1 0 7 4 9 9 3 2 5
      
  8. Ideu postupného delenia čísla desiatimi môžeš využiť aj na zisťovanie ciferného súčtu nejakého čísla. Napíš program, ktorý pre zadané číslo zistí jeho súčet cifier. Program bude zadané číslo postupne deliť 10 a spočítavať všetky zvyšky po delení 10.

    • napr. po spustení

      zadaj číslo: 523994701
      ciferný súčet je 40
      
  9. Napíš program, ktorý pre dané n vypočíta n! a zistí počet cifier a tiež prvú cifru tohto faktoriálu. Použi na to while-cyklus, v ktorom sa toto číslo stále delí 10 a počíta sa počet prechodov tohto cyklu. Po skončení cyklu by ti v premennej pre číslo mala ostať prvá cifra.

    • napr. po spustení

      zadaj n: 7
      prvá cifra je 5
      počet cifier je 4
      
      zadaj n: 1000
      prva cifra je 4
      pocet cifier je 2568
      
  10. Napíš program, ktorý pre dané celé číslo n nájde takú najbližšiu mocninu čísla 2, ktorá nie je menšia ako zadané n. Úlohu rieš pomocou while-cyklu, v ktorom v každom prechode cyklu vydelíš dané číslo 2. Program vypíše túto mocninu aj príslušný exponent. Nepoužívajte operáciu umocňovania **.

    • napr. po spustení

      zadaj číslo: 1024
      najbližšie je 2 ** 10 = 1024
      
      zadaj číslo: 2000
      najbližšie je 2 ** 11 = 2048
      
      zadaj číslo: 2
      najbližšie je 2 ** 1 = 2
      
  11. Tento program nakreslí rad n náhodne zafarbených kruhov.

    • pomocou for-cyklu:

      import tkinter
      from random import randrange
      
      sirka, vyska = 300, 250
      canvas = tkinter.Canvas(width=sirka, height=vyska)
      canvas.pack()
      
      n = 7
      x = 5
      for i in range(n):
          farba = f'#{randrange(256**3):06x}'
          canvas.create_oval(x, 5, x + 40, 45, fill=farba)
          x += 45
      
    • napr. po spustení:

      _images/04_17.png
    • Prepíš tento program pomocou while-cyklu.


  1. Prepíš predchádzajúci program pomocou while-cyklu tak, aby nekreslil n kruhov, ale maximálny počet kruhov, ktoré sa kompletne zmestia pre danú šírku (premenná sirka) grafickej plochy.

    • napr. po spustení:

      _images/04_18.png
  2. Toto je verzia vnorených cyklov, ktoré kreslia n x n kruhov bez ohľadu na veľkosť grafickej plochy.

    • pomocou for-cyklov:

      import tkinter
      from random import randrange
      
      sirka, vyska = 300, 250
      canvas = tkinter.Canvas(width=sirka, height=vyska)
      canvas.pack()
      
      n = 7
      y = 5
      for j in range(n):
          x = 5
          for i in range(n):
              farba = f'#{randrange(256**3):06x}'
              canvas.create_oval(x, y, x + 40, y + 40, fill=farba)
              x += 45
          y += 45
      
    • napr. po spustení:

      _images/04_19.png
    • Prepíš tento program pomocou while-cyklov bez premennej n tak, aby sa nakreslil maximálny počet kruhov, ktoré sa ale kompletne zmestia do grafickej plochy, napr. takto

      _images/04_20.png
  3. Využi program z prednášky, v ktorom sa vykresľovalo 1000 farebných bodiek podľa toho či mali x-ovú, alebo y-ovú súradnicu menšiu alebo väčšiu ako 150. Zmeň v tomto programe sériu príkazov if tak, aby sa kreslené bodky zafarbili takto (vnútorný červený štvorec má rozmery 150x150):

    • napr.

      _images/04_13.png
  4. Podobná úloha ako v predchádzajúcom príklade, len v tomto sa využívajú uhlopriečky štvorca. Asi tu budeš vedieť využiť podmienky x < y alebo 300 - x < y:

    • napr.

      _images/04_14.png
  5. V ďalšej verzii bodkovacej úlohy vybodkuješ kruh. Tu sa ti zíde vedieť počítať vzdialenosť kreslenej bodky od bodu (150, 150). Keď bude táto vzdialenosť menšia ako 135, bodka bude červená, inak modrá:

    • napr.

      _images/04_15.png

    Pomôcka: druhá mocnina vzdialenosti dvoch bodov (x1, y1) a (x2, y2) je (x1-x2)**2+(y1-y2)**2.

  6. Aj ďalšia verzia programu generuje náhodné bodky v 300 x 300. Bodiek teraz vygeneruj aspoň 10000 a zmenši ich na polomer 2. Č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át 4. Zamysli sa nad tým, prečo sa tento podiel blíži k číslu pi.

    • napr.

      _images/04_16.png
  7. Napíš program, ktorý najprv prečíta nejakú vetu a potom zistí počet výskytov písmena 'a' v tejto vete. Okrem toho program vypíše túto vetu tak, že všetky výskyty 'a' budú nahradené znakom '.'. Použi for-cyklus na prechádzanie jednotlivých znakov vety.

    • napr. po spustení

      zadaj vetu: tancuj, tancuj vykrucaj
      počet výskytov 'a' je 3
      nová veta:  t.ncuj, t.ncuj vykruc.j
      
  8. Predchádzajúci príklad doplň tak, aby sa nekontrolovalo len písmeno 'a', ale všetky samohlásky 'a', 'e', 'i', 'o', 'u'.

    • napr. po spustení

      zadaj vetu: sedi mucha na stene, sedi a spi
      počet výskytov samohlások je 11
      nová veta:  s.d. m.ch. n. st.n., s.d. . sp.
      
  9. Napíš program, ktorý v zadanej vete spočíta počet slov. Slová vo vete sú oddelené práve jednou medzerou.

    • napr. po spustení

      zadaj vetu: sedi mucha na stene, sedi a spi
      počet slov vo vete je 7
      
  10. Napíš program, ktorý v zadanej vete nájde dĺžku najdlhšieho slova. Slová vo vete sú oddelené práve jednou medzerou.

    • napr. po spustení

      zadaj vetu: v prvom rocniku programujeme v pythone
      dĺžka najdlhšieho slova vo vete je 12
      
  11. Fibonacciho postupnosť je postupnosť čísel, ktorá začína číslami 0 a 1 a každé ďalšie číslo sa vypočíta ako súčet dvoch predchádzajúcich.

    • niekoľko prvých členov postupnosti je

      0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, ...
      
    • Napíš program, ktorý nájde najmenšie fibonacciho číslo, ktoré je väčšie ako 1000000.


  1. Nájdi najväčšie číslo n také, že n! (n faktoriál) je menšie ako 1000000.


  1. Máme mince a bankovky s hodnotami 1, 2, 5, 10, 20, 50, 100. Napíš program, ktorý zistí, ako sa dá poskladať ľubovoľná suma peňazí minimálnym počtom kusov peňazí. Použi len jeden for-cyklus, v ktorom bude jeden if, nejaké priradenia a tiež print.

    • napr. po spustení

      zadaj cislo: 346
      3 krát hodnota 100
      2 krát hodnota 20
      1 krát hodnota 5
      1 krát hodnota 1
      
  2. Napíš program, ktorý hádže hracou kockou, vypisuje tieto padnuté čísla (od 1 do 6) a skončí vtedy, keď za sebou padnú tri šestky. Potom vypíše, koľkokrát musel hádzať.

    • napr. po spustení

      6 3 6 4 4 5 6 2 3 1 2 1 4 2 4 6 1 2 1 6 1 6 1 2 2 3 6 5 5 3 5 4 2 2 6 6 6
      počet hodov: 37
      
  3. 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ú dĺžky strán trojuholníka aspoň 30.

    • napr. pre

      a = 10, 100
      b = 250, 10
      c = 300, 230
      
    • po spustení:

      _images/04_21.png
    • Pomôcka: ak mám dva vrcholy (x1, y1) a (x2, y2), potom stred úsečky je x3, y3 = (x1+x2)/2, (y1+y2)/2