V této lekci se budeme věnovat cyklům for a while. Bez znalosti těchto dvou cyklů v podstatě není možné se věnovat programování.
Cyklus for¶
Cyklus for využijeme, pokud chceme nějaký příkaz vykonat opakovaně, ale pro různé hodnoty nějaké proměnné. Ukážeme si to na jednoduchém příkladě:
for i=1:1:5
i
endi = 1
i = 2
i = 3
i = 4
i = 5
Tento jednoduchý program nám postupně vypsal hodnoty od 1 do 5. Jak ale cyklus funguje?
Na prvním řádku za příkazem
forse volí, jaké hodnoty proměnnéibudeme uvažovat. Zápisem i=1:1:5 říkáme, že v proměnnéibudou postupně hodnoty od 1 (první parametr) do 5 (třetí parametr) s krokem 1 (druhý parametr). Pokud bychom tedy použili i=2:3:8, do proměnné i budeme postupně dosazovat hodnoty 2, 5 a 8.Následně dochází ke spuštění samotného těla cyklu (mezi rádkem začínájícím
fora řádkem obsahujícímfor). V našem případě se jedná o řádek, který obsahuje pouzei.Tělo cyklu bude postupně spuštěno a vyhodnoceno pro jednotlivé hodnoty proměnné
ina žačátku je
i=1, takže po zavoláníise vypíše 1. Program dojel na konec těly cyklu (kend) a proto se načte následující hodnotai, což je 2nyní je tedy
i=2a po zavoláníise tedy vypíše 2. Program opět dojel na konec těla cyklu a proto se opět načte následující hodnotai, což je 3nyní je tedy
i=3a po zavoláníise tedy vypíše 3. Program opět dojel na konec těla cyklu a proto se opět načte následující hodnotai, což je 4nyní je tedy
i=4a po zavoláníise tedy vypíše 4. Program opět dojel na konec těla cyklu a proto se opět načte následující hodnotai, což je 5nyní je tedy
i=5a po zavoláníise tedy vypíše 5. Program opět dojel na konec těla cyklu, ale protože 5 je poslední možná hodnota, cyklus se již nebude spouštet
Pro lepší názornost provedeme úpravu na druhém řádku, a napíšeme zde i^2.
for i=1:1:5
i^2
endans = 1
ans = 4
ans = 9
ans = 16
ans = 25
Jak tento program funguje?
Na prvním řádku za příkazem
forse volí, jaké hodnoty proměnnéibudeme uvažovat. Zápisem i=1:1:5 opět říkáme, že v proměnnéibudou postupně hodnoty od 1 (první parametr) do 5 (třetí parametr) s krokem 1 (druhý parametr).Tělo cyklu bude postupně spuštěno a vyhodnoceno pro jednotlivé hodnoty proměnné
ina žačátku je
i=1, takže po zavoláníi^2se vypíše druhá mocnina 1, což je 1. Program dojel na konec těly cyklu (kend) a proto se načte následující hodnotai, což je 2nyní je tedy
i=2a po zavoláníi^2se vypíše druhá mocnina 2, což je 2. Program opět dojel na konec těla cyklu a proto se opět načte následující hodnotai, což je 3nyní je tedy
i=3a po zavoláníi^2se vypíše druhá mocnina 3, což je 9. Program opět dojel na konec těla cyklu a proto se opět načte následující hodnotai, což je 4nyní je tedy
i=4a po zavoláníi^2se vypíše druhá mocnina 4, což je 16. Program opět dojel na konec těla cyklu a proto se opět načte následující hodnotai, což je 5nyní je tedy
i=5a po zavoláníi^2se vypíše druhá mocnina 5, což je 25. Program opět dojel na konec těla cyklu, ale protože 5 je poslední možná hodnota, cyklus se již nebude spouštet
Pokud bychom chtěli vypsat druhé mocniny čísel od 1 do 10, je třeba na prvním řádku upravit rozsah hodnot, které bude cyklus for uvažovat
for i=1:1:10
i^2
endans = 1
ans = 4
ans = 9
ans = 16
ans = 25
ans = 36
ans = 49
ans = 64
ans = 81
ans = 100
Následující úprava zajistí, že se nám vypíší pouze druhé mocniny sudých čísel od 2 do 20.
for i=2:2:20
i^2
endans = 4
ans = 16
ans = 36
ans = 64
ans = 100
ans = 144
ans = 196
ans = 256
ans = 324
ans = 400
Zatím jsem si hodnoty pouze vypisovali, což nám ale neumožňuje s výslednými hodnotami dále pracovat. Proto si nyní ukážeme, jak lze výsledky uložit do vektoru. Budeme chtít vypsat druhé mocniny čísel od 1 do 10 a uložit je do proměnné vektor:
%vytvorime desetiprvkový vektor (pojmenovany vektor), ktery bude obsahovat nuly
%zeros(a,b) vytvori nulovou matici, ktera ma "a" radku a "b" sloupcu
vektor=zeros(1,10);
for i=1:1:10
%hodnoty i^2 ukladame do `vektoru` na i-tou pozici
%vektor(i)= ... do vektoru 'vektor' uloz hodnotu na i-tou pozici
%na konci prikazu je strednik, aby se nevypisovaly jednotlive kroky cyklu for
vektor(i) = i^2;
end
%vypis vysledny vektor
vektorvektor =
1 4 9 16 25 36 49 64 81 100
Nyní máme všechny výsledky uložené v jednom vektoru a můžeme s nimi dále pracovat.
Program jde upravit tak, aby se do vektoru uložily druhé mocniny čísel od 1 do n:
%zde je zadana pozadovana hodnota n - horni mez
n=50
%velikost vektoru je urcena primo pomoci promenne n
vektor=zeros(1,n);
%obdobna uprava je i u cyklu `for`
for i=1:1:n
vektor(i) = i^2;
end
vektorn = 50
vektor =
Columns 1 through 11:
1 4 9 16 25 36 49 64 81 100 121
Columns 12 through 22:
144 169 196 225 256 289 324 361 400 441 484
Columns 23 through 33:
529 576 625 676 729 784 841 900 961 1024 1089
Columns 34 through 44:
1156 1225 1296 1369 1444 1521 1600 1681 1764 1849 1936
Columns 45 through 50:
2025 2116 2209 2304 2401 2500
Situace bude složitější, pokud bychom si chtěli ukládat pouze druhé mocniny sudých čísel od 2 do n.
Pokud použijeme stejný kód jako minule, výsledek nebude optimální
n=6
vektor=zeros(1,n)
for i=2:2:n
vektor(i)=i^2;
end
vektorn = 6
vektor =
0 0 0 0 0 0
vektor =
0 4 0 16 0 36
Do vektoru se nám sice uložily druhé mocniny, ale pouze na sudé pozice, liché obsahují nulu. To je dáno tím, že cyklus for je nastavený pouze pro sudá čísla, takže v příkazu vektor(i)=i^2 jsou hodnoty ukládány pouze na sudé pozice vektoru.
Řešení spočívá v následující úpravě:
n=8
%pri tvorbe nuloveho vektoru rikame, ze chceme pouze n/2 sloupcu.
%Prikaz floor zaokrouhli vyslednou hodnotu dolu (to je pro pripad, ze by nekdo jako n zadal liche cislo
vektor=zeros(1,floor(n/2));
for i=2:2:n
%obdobna uprava - i/2 zajisti, ze se bude ukladat i na licha cisla a bude se vse cislovat od 1
%pro i=2 dostane 1
%pro i=4 dostaneme 2
%pro i=6 dostaneme 3
%.......
vektor(i/2)=i^2;
end
vektorn = 8
vektor =
4 16 36 64
Alternativní způsob řešení problému.
Pomocí if zkontrolujeme, jestli není n liché (s využitím zbytku po celočíselném děleni (modulo)). Pokud je n liché, snížíme jeho hodnotu o jedna a pak již nemusíme zaokrouhlovat při tvorbě nulového vektoru.
n=7
%Kontrola zda je `n` liche - pokud ano, je hodnota `n` snížena o jedno (a dostavame sude cislo)
%povsimnete si, ze de vubec neni vetev `else;, protoze ji nepotrebujeme
if mod(n,2)==1
n=n-1
end
vektor=zeros(1,n/2)
for i=2:2:n
vektor(i/2)=i^2
end
vektorn = 7
n = 6
vektor =
0 0 0
vektor =
4 0 0
vektor =
4 16 0
vektor =
4 16 36
vektor =
4 16 36
Ukázka pokročilejšího kódu, který dle nastavených limitu počítá různé mocniny o různých základech:
pomocí proměnných
manse určí, jaké mocniny se budou počítat. Např. m=3 a n=6 znamená, že budou vypsány třetí až šesté mocninypomocí proměnných
iajse určí, pro jaké základy budou mocniny počítány. Např. i=2 a j=4 znamená, že budou počítány mocniny o základech dva až čtyři
%jake mocniny budeme pocitat
%nejmensi pocitana mocnina
m=3
%nejvetsi pocitana mocniny
n=6
%pro jake zaklady budeme mocniny pocitat
%nejmensi zaklad
i=2
%nejvetsi zaklad
j=4
%pripravime si nulovou matici, do ktere budeme ukladat vysledky
%na radcich budou jednotlive mocniny pro jeden zaklad
%tj. pokud budeme pocitat treti az seste mocniny, potrebujeme 4 sloupce
%na sloupcich budou mocniny jednotlivych zakladu
%tj. pokud budeme pocitat se zaklady 2,3 a 4, potrebujeme 4 radky
vektor=zeros(j-i+1,n-m+1);
%pouzivame dva cykly for
%prvni bude menit, jakou mocninu pocitame
for mocnina=m:1:n
%druhy cyklus meni zaklady
for zaklad=i:1:j
vektor(zaklad-(i-1),mocnina-(m-1))=zaklad^mocnina;
end
end
vektorm = 3
n = 6
i = 2
j = 4
vektor =
8 16 32 64
27 81 243 729
64 256 1024 4096
Výpočet faktoriálu s pomocí cyklu for¶
GNO/OCtave samozřejmě umí vypočítat faktoriál přímo, pomocí funkce factorial():
format long
factorial(70)ans = 1.197857166996989e+100
Příkaz format long byl přidán, protože jsme očekávali, že výsledná hodnota bude velká.
Nicméně my si ukážeme, jak počítat faktoriál s pomocí cyklu for na výpočtu faktoriálu 5. Výsledný kód není nikterak komplikovaný:
vysledek=1
for i=1:1:5
vysledek=vysledek*i
endvysledek = 1
vysledek = 1
vysledek = 2
vysledek = 6
vysledek = 24
vysledek = 120
Jak kód funguje?
Protože počítáme faktoriál pěti, využijeme cyklus
for, aby nám nageneroval všechny hodnotyiod 1 do pěty.Tyto hodnoty ale nepotřebujeme vypsat, potřebujeme je vynásobit mezi sebou.
Využijeme jednoduchý trik - na začátku jsme si vytvořili proměnnou
vysledeka uložili do něj 1.V těle cyklu for pak v každém kroku proměnnou
vysledekvynásobíme hodnotou v proměnnéia tuto nově vzniklou hdonotu uložíme dovýsledku(jak je vidět ve výpisu kódů, hodnota výsledku se podstupně zvětšuje)
Protože je takovýto program užitečný, uděláme z něj funkci. Vytvoříme nový skript pojmenovaný faktorial.m a do něj uložíme následujíci kód:
!POZOR - v názvu funkce použijte faktorial s k, funkce factorial s c již v GNU/Octave existuje, tak si ji nechceme přepsat
function out=faktorial(n)
vysledek=1;
for i=1:1:n
vysledek=vysledek*i;
end
out=vysledek;
endNyní když zadáme faktorial(n), obdržíme faktoriál čísla n.
faktorial(5)
ans = 120
Takovouto funkci můžeme využít například v situaci, že si chceme udělat tabulku faktoriálů od 1 do n
n= 10
vektor=zeros(1,n);
for p=1:1:n
vektor(p)=faktorial(p);
end
vektor
n = 10
vektor =
Columns 1 through 8:
1 2 6 24 120 720 5040 40320
Columns 9 and 10:
362880 3628800
Takový kód ale není moc optimální, protože pro každý faktoriál počítáme vše znovu (například pokud bychom počítaly prvních 100 faktoriálů, tak pro faktoriál 99 i faktoriál 100 bychom v obou případech nasobili čísla od 1 do 99).
Výrazné zjednodušení je představeno níže (zkuste si porovnat dobu výpočtu u obou kódů v případě větších n)
n=10
vysledek=zeros(1,n);
vysledek(1)=1;
for i=2:1:n
vysledek(i)=vysledek(i-1)*i;
end
vysledekn = 10
vysledek =
Columns 1 through 8:
1 2 6 24 120 720 5040 40320
Columns 9 and 10:
362880 3628800
Cyklus while¶
Jak je vidět, cyklus for je nesmírně užitečný. Někdy se ale můžeme dostat do situace, kdy nevíme kolikrát má být cyklus for vykonán (kolikrát a pro jaké hodnoty). V takové situaci můžeme využít cyklus while, který se spouští tak dlouho, dokud je splněná nějaká podmínka.
Jenoduchý příklad vidíme zde
i=1
%pokud je i mensi nez 10, telo cyklu se spusti
while i<10
%vypis na oprazovku "cyklus while se spustil"
display('cyklus while se spustil')
%zvedni hodnotu i o jedna
i=i+1
endi = 1
cyklus while se spustil
i = 2
cyklus while se spustil
i = 3
cyklus while se spustil
i = 4
cyklus while se spustil
i = 5
cyklus while se spustil
i = 6
cyklus while se spustil
i = 7
cyklus while se spustil
i = 8
cyklus while se spustil
i = 9
cyklus while se spustil
i = 10
Jak program funguje?
Na začátku programu jsme i nastavili na jedna
Pokud je
imenší než 10, cyklus se spustí (začne se opakovaně vykonávat tělo cyklu), dokud nebude i větší nebo rovno desetiCyklus vždy vypíše informaci o tom že se spustil a následně zvýší hodnotu
io jednaVidíme, že se cyklus spustil celkem 9x
V posledním (devátém) průběhu cyklu byla hodnota i zvětšena na 10, takže k dalšímu spuštění už nedošlo
Nyní si ukážeme další možnost použití cyklu while
Cheme najít první číslo větší než 100, které získáme tak že budeme k číslu jedna přičítat opakovaně číslo 7.
%pomocna promenna, kterou nastavujeme na 0
%jamile objevime hledane cislo, hodnota promenne zmenine na 1 (k tomu vyuzijeme `if`)
stop=0
%hodnota i je na zacatku 1
i=1
%cyklus while se bude spoustet tak dlouho, dokud bude proměnna "stop" rovna 0
while stop==0
%hodnotu i zvetsime o 7
i=i+7
%provedeme kontrolu, zda jiz hodnota i po poslednim pricteni neni vetsi nez 100
if i>100
%pokud i je vetsi nez 100, zmenime hodnotu stop na 1 (a while se jiz nespusti)
stop=1
end
endstop = 0
i = 1
i = 8
i = 15
i = 22
i = 29
i = 36
i = 43
i = 50
i = 57
i = 64
i = 71
i = 78
i = 85
i = 92
i = 99
i = 106
stop = 1
Nyní si ukážeme, jak pomocí while zjistit, kolik musíme sečíst po sobě jdoucích přirozených čísel od 1, aby byl součet větší než 10 000.
%do promenne `i` si budeme postupne ukladat jednotliva po sobe jdouci prirozena cisla, zacneme tedy s i=1
i=1;
%zde budeme ukaldat vysledny soucet, na zacatku je to tedy 1 (to naceteme primo z i)
vysledek=i;
%ptame se, zda je vysledny soucet mensi nez 10000, pokud ano, spusti se cyklus while a bude se opakovat, dokud soucat nebude vetsi
while vysledek<10000
%zvysime hodnotu i o 1
i=i+1;
%a o novou hodnotu i zvysime celkovy soucet
vysledek=vysledek+i;
end
%jakmile se program zastavi, vypise nam celkovy pocet prirzenych cisel
i
%a celkovy soucet
vysledek
%Muzeme si i vypsat nejvyssi pocet prirozenych cisel, ktery jeste nestaci + vysledny nedostatecny soucet
disp('posledni nedostatecne')
i-1
vysledek-ii = 141
vysledek = 10011
posledni nedostatecne
ans = 140
ans = 9870