Programovanie (2) v Jave
1-INF-166, letný semester 2023/24

Prednášky · Pravidlá · Softvér · Testovač
· Vyučujúcich predmetu možno kontaktovať mailom na adresách uvedených na hlavnej stránke. Hromadná mailová adresa zo zimného semestra v letnom semestri nefunguje.
· JavaFX: cesta k adresáru lib je v počítačových učebniach /usr/share/openjfx/lib.


Prednáška 3: Rozdiel medzi revíziami

Z Programovanie
Skočit na navigaci Skočit na vyhledávání
 
(27 medziľahlých úprav od 3 ďalších používateľov nie je zobrazených)
Riadok 1: Riadok 1:
 
== Oznamy ==
 
== Oznamy ==
  
* Budúci pondelok 5. októbra bude v čase prednášky teoretické cvičenie
+
Od tohto týždňa budú cvičenia v náhradných miestnostiach F2-295 a F1-223 (v piatok iba F2-295). Prosím príďte do jednej z nich a ak je už plná, skúste druhú.
** Budete písať krátky test zameraný učivo z prvých troch prednášok  
+
 
** Úspešní riešitelia testu pre pokročilých sa testu zúčastniť nemusia
+
Budúci pondelok 2. októbra bude na časti prednášky '''teoretické cvičenie'''
** Na teste bude povolené používať pero a ťahák vo forme jedného obojstranne popísaného listu A4
+
* Budete písať krátky test zameraný na učivo z prvých troch prednášok
 +
* Tento test sa počíta do druhých cvičení. Ak ich máte uznané z testu pre pokročilých, na test nemusíte ísť
 +
* Na teste bude povolené používať pero a ťahák vo forme jedného obojstranne popísaného listu A4
 +
 
 +
Ciele teoretického cvičenia
 +
* Precvičiť si poriadnejšie základné konštrukty
 +
* Vynechaním časti prednášky vznikne väčší priestor precvičiť si základy
 +
* Vyskúšať si prácu na papieri (bude treba na aj semestrálnom teste, ktorý má oveľa väčšiu váhu)
 +
 
 +
Prečo robíme toto cvičenie a písomné testy na papieri?
 +
* Dobré na precvičenie porozumenia hotovým programom (treba na ďalších predmetoch aj pri štúdiu odbornej literatúry)
 +
* Núti vás dobre si premyslieť každý detail, nie len náhodne skúšať meniť rôzne časti programu, kým nezačne fungovať
 +
* Pri zložitejších programoch sa už nedá postupovať náhodne
 +
* Na papieri miernejšie hodnotíme drobné chyby ako chýbajúca bodkočiarka, to vám na počítači pomôže nájsť kompilátor. Dôležitejšie je sa rozhodnúť, či má byť niekde napríklad <tt>i<n</tt> alebo <tt>i<=n</tt>.
  
 
== Opakovanie: výpočet faktoriálu ==
 
== Opakovanie: výpočet faktoriálu ==
Riadok 26: Riadok 39:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
'''Cvičenie:''' rozšírte program tak, aby okrem výpisu výsledku aj rozpísal faktoriál ako súčin:
 
<pre>
 
Zadajte n: 4
 
4! = 1*2*3*4 = 24
 
</pre>
 
 
Príklad behu programu pre ''n''=4:
 
Príklad behu programu pre ''n''=4:
 
<pre>
 
<pre>
Riadok 95: Riadok 103:
 
=== Vypisovanie deliteľov (podmienka v cykle) ===
 
=== Vypisovanie deliteľov (podmienka v cykle) ===
 
Nasledujúci program načíta od používateľa prirodzené číslo ''n'' a vypíše zoznam jeho deliteľov.
 
Nasledujúci program načíta od používateľa prirodzené číslo ''n'' a vypíše zoznam jeho deliteľov.
 +
* číslo ''i'' delí číslo ''n'' práve vtedy, keď zvyšok po delení čísla ''n'' číslom ''i'' je 0
  
 
<syntaxhighlight lang="C++">#include <iostream>
 
<syntaxhighlight lang="C++">#include <iostream>
Riadok 118: Riadok 127:
 
Delitele cisla 30: 1 2 3 5 6 10 15 30
 
Delitele cisla 30: 1 2 3 5 6 10 15 30
 
</pre>
 
</pre>
 +
 +
==Úprava a čitateľnosť programov==
 +
 +
Pri písaní programov myslite na to, že ich typicky budú okrem počítača čítať aj ľudia (napríklad vy po dlhšom čase, učitelia, alebo kolegovia na väčšom projekte). Je preto zvykom dodržiavať určité zásady, ktoré čitateľnosť zdrojového kódu zlepšujú:
 +
* '''Odsadzovanie:''' príkazy vykonávané v cykle, či v podmienke (alebo vo všeobecnosti v ľubovoľnom bloku medzi <tt>{</tt> a <tt>}</tt>) odsadzujeme o niekoľko pozícií doprava (napríklad o 4 medzery).
 +
** Pri vnorených cykloch, podmienkach a podobne odsadzujeme o ďalšie štyri pozície, atď.
 +
** Väčšina editorov a IDE pre C/C++ nejakým spôsobom odsadzovanie podporujú
 +
* '''Voľné riadky:''' ucelené časti programu je kvôli prehľadnosti často dobré oddeliť prázdnym riadkom.
 +
* '''Medzery:''' odporúča sa písať okolo operátorov medzery.
 +
* Napríklad zápis
 +
<syntaxhighlight lang="C++">
 +
for (int i = 1; i <= n; i++) {
 +
    a += i;
 +
}
 +
</syntaxhighlight>
 +
je o dosť prehľadnejší ako
 +
<syntaxhighlight lang="C++">
 +
for(int i=1;i<=n;i++){a+=i;}
 +
</syntaxhighlight>
 +
* '''Dĺžka riadku:''' odporúča sa vyhýbať sa riadkom dlhším ako 80 znakov. S dlhými riadkami sú problémy pri tlači alebo zobrazovaní v menších oknách; aj na veľkom monitore čitateľa zbytočne namáhajú. V prípade potreby je možné dlhšiu podmienku alebo iný výraz rozdeliť na viac riadkov.
 +
* '''Názvy premenných:''' najmä pri rozsiahlejších programoch je vhodné používať názvy premenných, ktoré vyjadrujú ich obsah (napríklad <tt>userCount</tt> alebo <tt>pocet_pouzivatelov</tt> namiesto <tt>h</tt>). Premenné v kratších programoch, alebo premenné používané iba lokálne v kratšom kuse programu možno označovať aj krátkymi zaužívanými názvami, ako napríklad <tt>i</tt> a <tt>j</tt> pre premenné v cykloch, <tt>n</tt> pre počet, alebo <tt>a</tt> pre pole.
 +
* '''Komentáre:''' význam jednotlivých úsekov kódu je najmä pri rozsiahlejších programoch dobré popísať v komentároch.
 +
 +
Pri známkovaní budeme brať do úvahy aj prehľadnosť vašich programov.
  
 
==Cyklus <tt>while</tt>==
 
==Cyklus <tt>while</tt>==
Riadok 132: Riadok 165:
 
# Ak áno, vykoná sa telo cyklu a celý proces sa opakuje (čiže sa opätovne vykoná overenie podmienky z bodu 1, atď.).
 
# Ak áno, vykoná sa telo cyklu a celý proces sa opakuje (čiže sa opätovne vykoná overenie podmienky z bodu 1, atď.).
 
# Ak nie, vykonávanie programu pokračuje prvým príkazom nasledujúcim za cyklom <tt>while</tt>.
 
# Ak nie, vykonávanie programu pokračuje prvým príkazom nasledujúcim za cyklom <tt>while</tt>.
 
=== Vypisovanie čísel od 1 po ''n'' pomocou while ===
 
 
Program z minulej prednášky, vypisujúci všetky prirodzené čísla od 1 po ''n'', môžeme napísať aj pomocou cyklu <tt>while</tt>:
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
using namespace std;
 
 
int main() {
 
    int n;
 
    cin >> n;
 
   
 
    // Premenna i bude obsahovat aktualne vypisovane cislo
 
    int i = 1;           
 
    while (i <= n) {     
 
        cout << " " << i;
 
        i++;           
 
    }
 
    cout << endl;
 
}
 
</syntaxhighlight>
 
  
 
=== Úroky v banke ===
 
=== Úroky v banke ===
Riadok 235: Riadok 247:
 
* Dokázali sme teda, že platí ''X ⊆ Y'' a súčasne ''X ⊆ Y''. Preto ''X = Y''. &#9723;
 
* Dokázali sme teda, že platí ''X ⊆ Y'' a súčasne ''X ⊆ Y''. Preto ''X = Y''. &#9723;
  
Poznámka: môže sa stať, že ''a'' mod ''b'' je 0. Nakoľko ale každé celé číslo delí nulu, gcd(''b'', 0), takže tvrdenie aj v tomto prípade platí.
+
Poznámka: môže sa stať, že ''a'' mod ''b'' je 0. Nakoľko ale každé celé číslo delí nulu, gcd(''b'', 0) = ''b'' pre každé kladné ''b''.
  
 
+
* Euklidov algoritmus opakovane používa lemu: gcd(12,8) = gcd(8, 4) = gcd(4, 0) = 4
 
+
* Dostáva sa k stále menším číslam, až kým ''b'' neklesne na nulu
Hodnotu gcd(''a'',''b'') teda možno nájsť ''Euklidovým algoritmom'' takto:
 
# Ak ''a'' mod ''b'' = 0, nutne gcd(''a'',''b'') = ''b''.
 
# V opačnom prípade vypočítame rovnakým spôsobom hodnotu gcd(''b'', ''a'' mod ''b'')
 
<span style="font-size:85%">(Všimnime si, že v bode 2 pre ''a < b'' platí ''a'' mod ''b'' = a, takže obidva argumenty sa iba vymenia. Prípad ''a = b'' tu nastať nemôže, lebo v takom prípade je splnená podmienka z bodu 1. Ak nakoniec ''a > b'', nutne ''a'' mod ''b'' < ''b'', takže prvý argument už bude aj naďalej väčší ako ten druhý. Keďže navyše ''b < a'' a ''a'' mod ''b'' < ''b'', sú obidva argumenty oproti pôvodným aspoň o 1 menšie. To garantuje, že sa Euklidov algoritmus v konečnom čase zastaví.)</span>
 
  
 
Implementácia tohto algoritmu teda môže vyzerať nasledovne:
 
Implementácia tohto algoritmu teda môže vyzerať nasledovne:
Riadok 278: Riadok 286:
 
2 0
 
2 0
 
</pre>
 
</pre>
 +
 +
Cvičenie: Ako bude fungovať Euklidov algoritmus pre vstupné čísla 8, 30?
  
 
===Nekonečný cyklus===
 
===Nekonečný cyklus===
Riadok 302: Riadok 312:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===Príklad č. 5: Hra &bdquo;hádaj číslo&rdquo;===
+
===Hra &bdquo;hádaj číslo&rdquo;===
 
V nasledujúcom programe si počítač &bdquo;myslí&rdquo; číslo od 1 do 100 a užívateľ háda, o ktoré číslo ide (až kým nakoniec neuhádne).  
 
V nasledujúcom programe si počítač &bdquo;myslí&rdquo; číslo od 1 do 100 a užívateľ háda, o ktoré číslo ide (až kým nakoniec neuhádne).  
  
Riadok 311: Riadok 321:
 
using namespace std;
 
using namespace std;
  
int main(void) {
+
int main() {
 
     srand(time(NULL));
 
     srand(time(NULL));
  
Riadok 331: Riadok 341:
 
         }
 
         }
 
     }
 
     }
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
''Príklad behu programu:''
+
Príklad behu programu:
 
<pre>
 
<pre>
 
Myslim si cislo od 1 po 100. Tvoj tip: 50
 
Myslim si cislo od 1 po 100. Tvoj tip: 50
Riadok 351: Riadok 360:
 
Tieto príkazy treba používať s mierou, keďže robia program menej prehľadným a sú častým zdrojom chýb.
 
Tieto príkazy treba používať s mierou, keďže robia program menej prehľadným a sú častým zdrojom chýb.
  
===Príklad: Hra &bdquo;hádaj číslo&rdquo; po druhé===
+
===Hra &bdquo;hádaj číslo&rdquo; s príkazom break===
  
 
Program uvedený vyššie môžeme prepísať bez boolovskej premennej s použitím nekonečného cyklu, z ktorého ale &bdquo;vyskočíme&rdquo; príkazom <tt>break</tt>, keď používateľ uhádne.
 
Program uvedený vyššie môžeme prepísať bez boolovskej premennej s použitím nekonečného cyklu, z ktorého ale &bdquo;vyskočíme&rdquo; príkazom <tt>break</tt>, keď používateľ uhádne.
Riadok 360: Riadok 369:
 
using namespace std;
 
using namespace std;
  
int main(void) {
+
int main() {
 
     srand(time(NULL));
 
     srand(time(NULL));
  
Riadok 378: Riadok 387:
 
         }
 
         }
 
     }
 
     }
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Riadok 403: Riadok 411:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
<span style="font-size:85%">(Drobnou nepodstatnou výnimkou z uvedeného tvrdenia je prípad, keď telo cyklu <tt>for</tt> obsahuje príkaz <tt>continue</tt>. V takom prípade sa príkaz <tt>prikaz2</tt> aj tak vykoná.)</span>
+
<span style="font-size:85%">(Drobnou výnimkou je prípad, keď telo cyklu <tt>for</tt> obsahuje príkaz <tt>continue</tt>. V takom prípade sa príkaz <tt>prikaz2</tt> aj tak vykoná.)</span>
  
Nasledujúce kúsky kódu napríklad obidva vypisujú čísla 0 až 9:
+
Nasledujúce kúsky kódu napríklad obidva vypisujú čísla 1 až 9:
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
for (int i = 0; i <= 9; i++) {
+
for (int i = 1; i <= 9; i++) {
 
     cout << " " << i;
 
     cout << " " << i;
 
}
 
}
Riadok 413: Riadok 421:
  
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
int i = 0;
+
int i = 1;
 
while (i <= 9) {
 
while (i <= 9) {
 
     cout << " " << i;
 
     cout << " " << i;
Riadok 422: Riadok 430:
 
Cyklus <tt>for</tt> spravidla používame, ak má náš cyklus krátky a jednoduchý iterátor (<tt>prikaz2</tt>) a jednoduchú podmienku. V opačnom prípade väčšinou používame cyklus <tt>while</tt>.
 
Cyklus <tt>for</tt> spravidla používame, ak má náš cyklus krátky a jednoduchý iterátor (<tt>prikaz2</tt>) a jednoduchú podmienku. V opačnom prípade väčšinou používame cyklus <tt>while</tt>.
  
===Príklad č. 1: Vypisovanie deliteľov po druhé===
+
===Vypisovanie deliteľov od najväčších===
  
 
Vráťme sa k programu na vypisovanie všetkých deliteľov čísla ''n'' z úvodu tejto prednášky. Tam sme deliteľov vypisovali v poradí od najmenšieho po najväčší, a to použitím cyklu
 
Vráťme sa k programu na vypisovanie všetkých deliteľov čísla ''n'' z úvodu tejto prednášky. Tam sme deliteľov vypisovali v poradí od najmenšieho po najväčší, a to použitím cyklu
Riadok 430: Riadok 438:
 
Nahradením tohto cyklu cyklom
 
Nahradením tohto cyklu cyklom
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
for (int i = n; i > 0; i--)
+
for (int i = n; i >= 1; i--)
 
</syntaxhighlight>
 
</syntaxhighlight>
 
získame program vypisujúci delitele v poradí od najväčšieho po najmenší.
 
získame program vypisujúci delitele v poradí od najväčšieho po najmenší.
Riadok 438: Riadok 446:
 
using namespace std;
 
using namespace std;
  
int main(void) {
+
int main() {
 
     int n;
 
     int n;
 
     cout << "Zadajte cislo: ";
 
     cout << "Zadajte cislo: ";
Riadok 444: Riadok 452:
 
      
 
      
 
     cout << "Delitele cisla " << n << ":";
 
     cout << "Delitele cisla " << n << ":";
     for (int i = n; i > 0; i--) {
+
     for (int i = n; i >= 1; i--) {
 
         if (n % i == 0) {
 
         if (n % i == 0) {
 
             cout << " " << i;
 
             cout << " " << i;
Riadok 450: Riadok 458:
 
     }
 
     }
 
     cout << endl;
 
     cout << endl;
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
''Príklad behu programu:''
+
Príklad behu programu:
 
<pre>
 
<pre>
 
Zadajte cislo: 30
 
Zadajte cislo: 30
Delitelia cisla 30: 30 15 10 6 5 3 2 1
+
Delitele cisla 30: 30 15 10 6 5 3 2 1
 
</pre>
 
</pre>
  
===Príklad č. 2: Vypisovanie deliteľov po tretie===
+
===Rýchlejšie hľadanie deliteľov===
  
Program na vypisovanie deliteľov môžeme o niečo urýchliť: stačí si všimnúť, že ''i'' je deliteľ čísla ''n'' práve vtedy, keď je deliteľom čísla ''n'' číslo ''n/i''; číslo ''n/i'' teda môžeme rovno vypísať spoločne s ''i''. Aspoň jedno z tejto dvojice čísel je navyše menšie alebo rovné odmocnine z ''n'', čo znamená, že stačí prehľadať iba čísla ''i'' spĺňajúce túto podmienku.   
+
Program na vypisovanie deliteľov môžeme o niečo urýchliť:  
 +
* Stačí si všimnúť, že ''i'' je deliteľ čísla ''n'' práve vtedy, keď ''n/i'' je deliteľom čísla ''n''.
 +
* Číslo ''n/i'' teda môžeme rovno vypísať spoločne s ''i''.  
 +
* Aspoň jedno z tejto dvojice čísel je navyše menšie alebo rovné odmocnine z ''n'', čo znamená, že stačí prehľadať iba čísla ''i'' spĺňajúce túto podmienku.   
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
  
int main(void) {
+
int main() {
 
     int n;
 
     int n;
 
     cout << "Zadajte cislo: ";
 
     cout << "Zadajte cislo: ";
 
     cin >> n;
 
     cin >> n;
 
      
 
      
     cout << "Delitelia cisla " << n << ":";
+
     cout << "Delitele cisla " << n << ":";
 
     for (int i = 1; i*i <= n; i++) {
 
     for (int i = 1; i*i <= n; i++) {
 
         if (n % i == 0) {
 
         if (n % i == 0) {
Riadok 479: Riadok 489:
 
     }
 
     }
 
     cout << endl;
 
     cout << endl;
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
''Cvičenie 1:'' Občas sa môže stať, že program vypíše niektorého deliteľa dvakrát. Kedy? Modifikujte telo cyklu <tt>for</tt> tak, aby program každého deliteľa vypísal práve raz.
+
Cvičenie 1: Občas sa môže stať, že program vypíše niektorého deliteľa dvakrát. Kedy? Modifikujte telo cyklu <tt>for</tt> tak, aby program každého deliteľa vypísal práve raz.
  
''Cvičenie 2:'' Vyskúšajte si rýchlosť rôznych variantov programu na vypisovanie deliteľov na veľkom vstupe, napríklad pre ''n'' = 1234567890.
+
Cvičenie 2: Vyskúšajte si rýchlosť rôznych variantov programu na vypisovanie deliteľov na veľkom vstupe, napríklad pre ''n'' = 1234567890.
  
===Príklad č. 3: Nekonečný cyklus <tt>for</tt>===
+
===Nekonečný cyklus <tt>for</tt>===
  
 
V cykle
 
V cykle
Riadok 494: Riadok 503:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
môžu byť príkazy <tt>prikaz1</tt> a <tt>prikaz2</tt> aj prázdne &ndash; v takom prípade sa na ich mieste nič nevykoná. Podobne môže byť prázdna aj podmienka <tt>podmienka</tt>, ktorá sa v takom prípade interpretuje ako <tt>true</tt>. Z uvedeného vyplýva, že nekonečný cyklus možno napísať aj ako
+
môžu byť príkazy <tt>prikaz1</tt> a <tt>prikaz2</tt> aj prázdne &ndash; v takom prípade sa na ich mieste nič nevykoná. Podobne môže byť prázdna aj podmienka <tt>podmienka</tt>, ktorá sa v takom prípade interpretuje ako <tt>true</tt>.  
 +
 
 +
Nekonečný cyklus možno teda napísať aj ako
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
 
for ( ; true; ) {
 
for ( ; true; ) {
Riadok 500: Riadok 511:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
prípadne ekvivalentne ako
+
prípadne ako
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
 
for ( ; ; ) {
 
for ( ; ; ) {
Riadok 509: Riadok 520:
 
==Vnorené cykly==
 
==Vnorené cykly==
  
Vykreslíme &bdquo;štvorec&rdquo; pozostávajúci z <tt>n</tt> krát <tt>n</tt> čísel, ktoré sú striedavo 0 a 1, podobne ako biele a čierne políčka na šachovnici.
+
Vypíšeme tabuľku násobilky, v ktorej bude v riadku ''i'' a stĺpci ''j'' súčin ''i ⋅ j''.  
* Potrebujeme na to dva vnorené cykly: jeden pôjde cez riadky, druhý cez stĺpce.
+
* Použijeme dva vnorené cykly: jeden pôjde cez riadky, druhý cez stĺpce.
* Ak je hodnota <tt>row + column</tt> párna, píšeme 0; inak píšeme 1.
 
  
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
Riadok 517: Riadok 527:
 
using namespace std;
 
using namespace std;
  
int main(void) {
+
int main() {
     int n;
+
     int n; // pokial ma ist nasobilka
 
     cin >> n;
 
     cin >> n;
     for (int row = 0; row <= n - 1; row++) {
+
     for (int riadok = 1; riadok <= n; riadok++) {
         for (int column = 0; column <= n - 1; column++) {
+
         for (int stlpec = 1; stlpec <= n; stlpec++) {
             if ((row + column) % 2 == 0) {
+
             cout << " " << riadok * stlpec;
                cout << "0";
 
            } else {
 
                cout << "1";
 
            }
 
 
         }
 
         }
 
         cout << endl;
 
         cout << endl;
 
     }
 
     }
   
 
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Úprava a čitateľnosť programov==
+
Ukážka výstupu pre ''n''=5:
 
+
<pre>
Pri písaní programov je potrebné myslieť na to, že ho typicky budú okrem počítača čítať aj ľudia (či už ide o vás samotných po dlhšom čase, učiteľov, alebo v praxi o spolupracovníkov na väčšom projekte). Je preto zvykom dodržiavať určité zásady, ktoré čitateľnosť zdrojového kódu zlepšujú:
+
1 2 3 4 5
* '''Odsadzovanie:''' príkazy vykonávané v cykle, či v podmienke (alebo vo všeobecnosti v ľubovoľnom bloku medzi <tt>{</tt> a <tt>}</tt>) kvôli čitateľnosti odsadzujeme o niekoľko pozícií doprava (napríklad o 4 medzery). Pri vnorených cykloch, podmienkach a podobne odsadzujeme o ďalšie štyri pozície, atď. Väčšina syntax zvýrazňujúcich textových editorov a IDE pre C/C++ nejakým spôsobom odsadzovanie podporujú (minimálne by sa mala dať nastaviť šírka tabulátora).
+
2 4 6 8 10
* '''Voľné riadky:''' ucelené časti programu je kvôli prehľadnosti často dobré oddeliť prázdnym riadkom.
+
3 6 9 12 15
* '''Medzery:''' odporúča sa (najmä v zložitejších výrazoch) písať okolo operátorov medzery.
+
4 8 12 16 20
* Napríklad zápis
+
5 10 15 20 25
<syntaxhighlight lang="C++">
+
</pre>
for (int i = 0; i <= count - 1; i++) {
+
Ak pred každé číslo vypíšeme namiesto medzery <tt>" "</tt> tabulátor <tt>"\t"</tt>, dostaneme krajší výstup
    a += i;
+
<pre>
}
+
1 2 3 4 5
</syntaxhighlight>
+
2 4 6 8 10
je o dosť prehľadnejší ako
+
3 6 9 12 15
<syntaxhighlight lang="C++">
+
4 8 12 16 20
for(int i=0;i<=count-1;i++){a+=i;}
+
5 10 15 20 25
</syntaxhighlight>
+
</pre>
* '''Dĺžka riadku:''' odporúča sa vyhýbať sa riadkom dlhším ako 80 znakov (aj keď nie vždy sa tento limit dá dodržať striktne). S dlhými riadkami sú problémy pri tlači alebo zobrazovaní v menších oknách; aj na veľkom monitore čitateľa zbytočne namáhajú. V prípade potreby je možné dlhšiu podmienku alebo iný výraz rozdeliť na viac riadkov.
 
* '''Názvy premenných:''' najmä pri rozsiahlejších programoch je vhodné používať názvy premenných, ktoré vyjadrujú ich obsah (napríklad <tt>userCount</tt> alebo <tt>pocet_pouzivatelov</tt> namiesto <tt>h</tt>). Premenné v kratších programoch, alebo premenné používané iba lokálne v kratšom kuse programu možno označovať aj krátkymi zaužívanými názvami, ako napríklad <tt>i</tt> a <tt>j</tt> pre premenné v cykloch, <tt>n</tt> pre počet, alebo <tt>a</tt> pre pole.
 
* '''Komentáre:''' význam jednotlivých úsekov kódu je najmä pri rozsiahlejších programoch dobré popísať v komentároch.
 
  
Pri známkovaní budeme brať do úvahy aj prehľadnosť vašich programov.
+
Cvičenie: pridajme jednotlivým riadkom a stĺpcom hlavičku
  
 
==Cykly: zhrnutie==
 
==Cykly: zhrnutie==

Aktuálna revízia z 19:38, 22. september 2023

Oznamy

Od tohto týždňa budú cvičenia v náhradných miestnostiach F2-295 a F1-223 (v piatok iba F2-295). Prosím príďte do jednej z nich a ak je už plná, skúste druhú.

Budúci pondelok 2. októbra bude na časti prednášky teoretické cvičenie

  • Budete písať krátky test zameraný na učivo z prvých troch prednášok
  • Tento test sa počíta do druhých cvičení. Ak ich máte uznané z testu pre pokročilých, na test nemusíte ísť
  • Na teste bude povolené používať pero a ťahák vo forme jedného obojstranne popísaného listu A4

Ciele teoretického cvičenia

  • Precvičiť si poriadnejšie základné konštrukty
  • Vynechaním časti prednášky vznikne väčší priestor precvičiť si základy
  • Vyskúšať si prácu na papieri (bude treba na aj semestrálnom teste, ktorý má oveľa väčšiu váhu)

Prečo robíme toto cvičenie a písomné testy na papieri?

  • Dobré na precvičenie porozumenia hotovým programom (treba na ďalších predmetoch aj pri štúdiu odbornej literatúry)
  • Núti vás dobre si premyslieť každý detail, nie len náhodne skúšať meniť rôzne časti programu, kým nezačne fungovať
  • Pri zložitejších programoch sa už nedá postupovať náhodne
  • Na papieri miernejšie hodnotíme drobné chyby ako chýbajúca bodkočiarka, to vám na počítači pomôže nájsť kompilátor. Dôležitejšie je sa rozhodnúť, či má byť niekde napríklad i<n alebo i<=n.

Opakovanie: výpočet faktoriálu

#include <iostream>
using namespace std;

int main() {
    int n;
    
    cout << "Zadajte n: ";
    cin >> n;
    
    int vysledok = 1;
    for (int i = 1; i <= n; i++) {
       vysledok = vysledok * i;
    }
    
    cout << n << "! = " << vysledok << endl;
}

Príklad behu programu pre n=4:

Zadajte n: 4
4! = 24

Cvičenie: rozšírte program tak, aby okrem výpisu výsledku aj rozpísal faktoriál ako súčin:

Zadajte n: 4
4! = 1*2*3*4 = 24


Ďalšie príklady na cyklus for

Simulovanie hodov kocky

Nasledujúci program od používateľa načíta číslo n a vypíše n simulovaných hodov kocky (každý na samostatný riadok).

#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

int main() {
    // Inicializacia generatora pseudonahodnych cisel
    srand(time(NULL)); 
 
    int n;
    cout << "Zadajte pocet hodov: ";
    cin >> n;
    
    for (int i = 1; i <= n; i++) {
        // Vygenerovanie a vypisanie hodu kockou
        cout << rand() % 6 + 1 << endl; 
    }
}

Príklad behu programu:

Zadajte pocet hodov: 6
2
6
1
2
1
4
  • Program využíva funkciu rand(), ktorá generuje pseudonáhodné celé čísla.
    • Nie sú v skutočnosti náhodné, lebo ide o pevne definovanú matematickú postupnosť, ktorá však má mnohé vlastnosti náhodných čísel.
    • Aby bolo možné použiť túto funkciu, treba do hlavičky pridať #include <cstdlib>.
    • Výstupom funkcie rand() je nezáporné celé číslo medzi nulou a nejakou veľkou konštantou.
    • Zvyšok po delení tohto čísla šiestimi, t.j. rand() % 6, je potom číslo medzi 0 a 5. Ak k tomu pripočítame 1, dostaneme číslo od 1 po 6.
  • Funkcia srand inicializuje generátor pseudonáhodných čísel na základe parametra určujúceho počiatočný bod pseudonáhodnej postupnosti.
    • My ako tento parameter používame aktuálny čas (v sekundách od začiatku roku 1970), čo zaručuje dostatočný efekt náhodnosti.
    • Aby bolo možné použiť funkciu time, je treba do hlavičky pridať #include <ctime>.

Vypisovanie deliteľov (podmienka v cykle)

Nasledujúci program načíta od používateľa prirodzené číslo n a vypíše zoznam jeho deliteľov.

  • číslo i delí číslo n práve vtedy, keď zvyšok po delení čísla n číslom i je 0
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "Zadajte cislo: ";
    cin >> n;
    
    cout << "Delitele cisla " << n << ":";
    for (int i = 1; i <= n; i++) {
        if (n % i == 0) {
            cout << " " << i;
        }
    }    
    cout << endl;
}

Príklad behu programu:

Zadajte cislo: 30
Delitele cisla 30: 1 2 3 5 6 10 15 30

Úprava a čitateľnosť programov

Pri písaní programov myslite na to, že ich typicky budú okrem počítača čítať aj ľudia (napríklad vy po dlhšom čase, učitelia, alebo kolegovia na väčšom projekte). Je preto zvykom dodržiavať určité zásady, ktoré čitateľnosť zdrojového kódu zlepšujú:

  • Odsadzovanie: príkazy vykonávané v cykle, či v podmienke (alebo vo všeobecnosti v ľubovoľnom bloku medzi { a }) odsadzujeme o niekoľko pozícií doprava (napríklad o 4 medzery).
    • Pri vnorených cykloch, podmienkach a podobne odsadzujeme o ďalšie štyri pozície, atď.
    • Väčšina editorov a IDE pre C/C++ nejakým spôsobom odsadzovanie podporujú
  • Voľné riadky: ucelené časti programu je kvôli prehľadnosti často dobré oddeliť prázdnym riadkom.
  • Medzery: odporúča sa písať okolo operátorov medzery.
  • Napríklad zápis
for (int i = 1; i <= n; i++) {
    a += i;
}

je o dosť prehľadnejší ako

for(int i=1;i<=n;i++){a+=i;}
  • Dĺžka riadku: odporúča sa vyhýbať sa riadkom dlhším ako 80 znakov. S dlhými riadkami sú problémy pri tlači alebo zobrazovaní v menších oknách; aj na veľkom monitore čitateľa zbytočne namáhajú. V prípade potreby je možné dlhšiu podmienku alebo iný výraz rozdeliť na viac riadkov.
  • Názvy premenných: najmä pri rozsiahlejších programoch je vhodné používať názvy premenných, ktoré vyjadrujú ich obsah (napríklad userCount alebo pocet_pouzivatelov namiesto h). Premenné v kratších programoch, alebo premenné používané iba lokálne v kratšom kuse programu možno označovať aj krátkymi zaužívanými názvami, ako napríklad i a j pre premenné v cykloch, n pre počet, alebo a pre pole.
  • Komentáre: význam jednotlivých úsekov kódu je najmä pri rozsiahlejších programoch dobré popísať v komentároch.

Pri známkovaní budeme brať do úvahy aj prehľadnosť vašich programov.

Cyklus while

Okrem cyklu for možno v jazykoch C/C++ používať aj cyklus while s nasledujúcou schémou:

while (podmienka) { 
    telo cyklu 
}

Telo takéhoto cyklu sa vykonáva, kým je podmienka cyklu splnená. Presnejšie sa pri vykonávaní cyklu while typicky deje nasledovné:

  1. Overí sa, či je podmienka cyklu splnená.
  2. Ak áno, vykoná sa telo cyklu a celý proces sa opakuje (čiže sa opätovne vykoná overenie podmienky z bodu 1, atď.).
  3. Ak nie, vykonávanie programu pokračuje prvým príkazom nasledujúcim za cyklom while.

Úroky v banke

  • Predpokladajme, že na začiatku každého roku uložíme na účet nejakú pevnú sumu (napríklad 1000 EUR).
  • Na konci každého roku sa vklad zúročí ročným úrokom (napríklad 5%).
  • Za koľko rokov úspory dosiahnu danú cieľovú čiastku (napríklad 10000 EUR)?

Túto úlohu budeme riešiť programom pracujúcim nasledovne:

  • V premennej ciastka budeme uchovávať aktuálny stav účtu
  • V každom roku túto premennú zvýšime o vklad a úrok
  • Toto opakujeme, kým suma uložená v premennej nie je rovná aspoň cieľovej čiastke.

To môžeme vyjadriť pomocou cyklu while.

#include <iostream>
using namespace std;

int main(void) {
    double vklad, ciel, urok;
        
    cout << "Zadaj kazdorocny vklad: ";
    cin >> vklad;
    cout << "Zadaj cielovu ciastku: ";
    cin >> ciel;
    cout << "Zadaj rocny urok (v %): ";
    cin >> urok;
    
    int rok = 0;
    double ciastka = 0;
    
    while (ciastka < ciel) {
        rok++;                   
        ciastka = (ciastka + vklad) * (1 + urok / 100);
        cout << "Na konci roku " << rok << " je na ucte " << ciastka << " EUR." << endl;  
    }
}

Ukážkový vstup a výstup:

Zadaj kazdorocny vklad: 1000
Zadaj cielovu ciastku: 10000
Zadaj rocny urok (v %): 5
Na konci roku 1 je na ucte 1050 EUR
Na konci roku 2 je na ucte 2152.5 EUR
Na konci roku 3 je na ucte 3310.12 EUR
Na konci roku 4 je na ucte 4525.63 EUR
Na konci roku 5 je na ucte 5801.91 EUR
Na konci roku 6 je na ucte 7142.01 EUR
Na konci roku 7 je na ucte 8549.11 EUR
Na konci roku 8 je na ucte 10026.6 EUR

Euklidov algoritmus

Euklidov algoritmus slúži na hľadanie najväčšieho spoločného deliteľa dvojice kladných celých čísel a, b.

  • To znamená: hľadáme najväčšie kladné prirodzené číslo d, ktoré delí súčasne a aj b.
  • Najväčší spoločný deliteľ čísel a, b označujeme gcd(a,b) (z angl. greatest common divisor). V slovenčine sa používa aj s označenie nsd(a,b).
  • Ide o jeden z najstarších známych algoritmov vôbec. Jeho variant popísal už Euklides v diele Základy okolo roku 300 pred Kr.

Príklad:

  • Delitele čísla 12 sú 1, 2, 3, 4, 6, 12.
  • Delitele čísla 8 sú 1, 2, 4, 8.
  • Spoločné delitele 8 a 12 teda sú 1, 2, 4.
  • Najväčší spoločný deliteľ čísel 8 a 12 je teda gcd(8,12) = 4.


Euklidov algoritmus je založený na platnosti nasledujúcej lemy.

Lema. Pre všetky kladné celé čísla a, b platí:

gcd(a,b) = gcd(b, a mod b).

Dôkaz.

  • Nech X je množina spoločných deliteľov a a b, nech Y je množina spoločných deliteľov b a a mod b. Dokážeme rovnosť X = Y.
  • Ak označíme r := a mod b, tak existuje celé číslo q také, že a = q b + r.
  • Ak x ∈ Y, číslo x delí b aj r a z rovnosti a = q b + r vyplýva, že delí aj a. Teda x ∈ X.
  • Ak naopak x ∈ X, číslo x delí a aj b a z rovnosti r = a - qb vyplýva, že delí aj r. Preto x ∈ Y.
  • Dokázali sme teda, že platí X ⊆ Y a súčasne X ⊆ Y. Preto X = Y. ◻

Poznámka: môže sa stať, že a mod b je 0. Nakoľko ale každé celé číslo delí nulu, gcd(b, 0) = b pre každé kladné b.

  • Euklidov algoritmus opakovane používa lemu: gcd(12,8) = gcd(8, 4) = gcd(4, 0) = 4
  • Dostáva sa k stále menším číslam, až kým b neklesne na nulu

Implementácia tohto algoritmu teda môže vyzerať nasledovne:

#include <iostream>
using namespace std;

int main() {
    int a, b;    
    cout << "Zadaj dvojicu kladnych celych cisel: ";
    cin >> a >> b;
    
    while (b != 0) {
        int r = a % b;
        a = b;
        b = r;
    }
    
    cout << "Najvacsi spolocny delitel je " << a << "." << endl;
}

Príklad behu programu:

Zadaj dvojicu kladnych celych cisel: 30 8
Najvacsi spolocny delitel je 2.

Tento výpočet prešiel cez dvojice:

30 8
8 6
6 2
2 0

Cvičenie: Ako bude fungovať Euklidov algoritmus pre vstupné čísla 8, 30?

Nekonečný cyklus

V cykle while typu

while (true) {
    telo cyklu
}

je podmienka stále splnená; telo cyklu sa teda opakuje donekonečna resp. kým program nezastavíme (v prípade, že cyklus neukončíme umelo príkazom break, ktorý uvidíme o chvíľu).

Napríklad môžeme donekonečna niečo vypisovať:

#include <iostream>
using namespace std;

int main() {
    while (true) {
        cout << "Hello, World!" << endl;
    }
}

Hra „hádaj číslo”

V nasledujúcom programe si počítač „myslí” číslo od 1 do 100 a užívateľ háda, o ktoré číslo ide (až kým nakoniec neuhádne).

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main() {
    srand(time(NULL));

    int num = rand() % 100 + 1;
    cout << "Myslim si cislo od 1 po 100. Tvoj tip: ";
    
    bool correct = false;

    while (!correct) {
        int guess;
        cin >> guess;
        if (guess < num) {
            cout << "Prilis nizko. Iny tip: ";
        } else if (guess > num) {
            cout << "Prilis vysoko. Iny tip: ";        
        } else {
            correct = true;
            cout << "Spravne!" << endl;
        }
    }
}

Príklad behu programu:

Myslim si cislo od 1 po 100. Tvoj tip: 50
Prilis nizko. Iny tip: 70
Prilis nizko. Iny tip: 90
Prilis vysoko. Iny tip: 80
Spravne!

Príkazy break a continue

V C/C++ existuje dvojica príkazov umožňujúcich umelo prerušiť vykonávanie cyklu resp. jednej jeho iterácie:

  • Príkaz break ukončí cyklus, v ktorom sa program práve nachádza; vykonávanie programu pokračuje prvým príkazom za cyklom.
  • Príkaz continue „skočí” na ďalšiu iteráciu cyklu, pričom nevykoná zvyšok tela cyklu.

Tieto príkazy treba používať s mierou, keďže robia program menej prehľadným a sú častým zdrojom chýb.

Hra „hádaj číslo” s príkazom break

Program uvedený vyššie môžeme prepísať bez boolovskej premennej s použitím nekonečného cyklu, z ktorého ale „vyskočíme” príkazom break, keď používateľ uhádne.

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main() {
    srand(time(NULL));

    int num = rand() % 100 + 1;
    cout << "Myslim si cislo od 1 po 100. Tvoj tip: ";
    
    while (true) {
        int guess;
        cin >> guess;
        if (guess < num) {
            cout << "Prilis nizko. Iny tip: ";
        } else if (guess > num) {
            cout << "Prilis vysoko. Iny tip: ";        
        } else {
            cout << "Spravne!" << endl;
            break;
        }
    }
}

Viac o cykle for

Schéma cyklu for

Cyklus for sme doposiaľ používali iba veľmi základným spôsobom; jeho možnosti sú omnoho väčšia. Vo všeobecnosti možno schému cyklu for popísať nasledovne:

for (prikaz1; podmienka; prikaz2) {
    postupnost_prikazov
}

Takýto cyklus potom pracuje nasledovne:

  • Vykoná sa príkaz prikaz1.
  • Kým platí podmienka podmienka, vykonáva sa telo cyklu postupnost_prikazov zakaždým nasledované príkazom prikaz2.

Uvedený cyklus for je teda typicky ekvivalentný nasledujúcemu cyklu while:

prikaz1;
while (podmienka) {
    postupnost_prikazov
    prikaz2;
}

(Drobnou výnimkou je prípad, keď telo cyklu for obsahuje príkaz continue. V takom prípade sa príkaz prikaz2 aj tak vykoná.)

Nasledujúce kúsky kódu napríklad obidva vypisujú čísla 1 až 9:

for (int i = 1; i <= 9; i++) {
    cout << " " << i;
}
int i = 1;
while (i <= 9) {
    cout << " " << i;
    i++;
}

Cyklus for spravidla používame, ak má náš cyklus krátky a jednoduchý iterátor (prikaz2) a jednoduchú podmienku. V opačnom prípade väčšinou používame cyklus while.

Vypisovanie deliteľov od najväčších

Vráťme sa k programu na vypisovanie všetkých deliteľov čísla n z úvodu tejto prednášky. Tam sme deliteľov vypisovali v poradí od najmenšieho po najväčší, a to použitím cyklu

for (int i = 1; i <= n; i++)

Nahradením tohto cyklu cyklom

for (int i = n; i >= 1; i--)

získame program vypisujúci delitele v poradí od najväčšieho po najmenší.

#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "Zadajte cislo: ";
    cin >> n;
    
    cout << "Delitele cisla " << n << ":";
    for (int i = n; i >= 1; i--) {
        if (n % i == 0) {
            cout << " " << i;
        }
    }
    cout << endl;
}

Príklad behu programu:

Zadajte cislo: 30
Delitele cisla 30: 30 15 10 6 5 3 2 1

Rýchlejšie hľadanie deliteľov

Program na vypisovanie deliteľov môžeme o niečo urýchliť:

  • Stačí si všimnúť, že i je deliteľ čísla n práve vtedy, keď n/i je deliteľom čísla n.
  • Číslo n/i teda môžeme rovno vypísať spoločne s i.
  • Aspoň jedno z tejto dvojice čísel je navyše menšie alebo rovné odmocnine z n, čo znamená, že stačí prehľadať iba čísla i spĺňajúce túto podmienku.
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "Zadajte cislo: ";
    cin >> n;
    
    cout << "Delitele cisla " << n << ":";
    for (int i = 1; i*i <= n; i++) {
        if (n % i == 0) {
            cout << " " << i << " " << n / i;
        }
    }
    cout << endl;
}

Cvičenie 1: Občas sa môže stať, že program vypíše niektorého deliteľa dvakrát. Kedy? Modifikujte telo cyklu for tak, aby program každého deliteľa vypísal práve raz.

Cvičenie 2: Vyskúšajte si rýchlosť rôznych variantov programu na vypisovanie deliteľov na veľkom vstupe, napríklad pre n = 1234567890.

Nekonečný cyklus for

V cykle

for (prikaz1; podmienka; prikaz2) {
    postupnost_prikazov
}

môžu byť príkazy prikaz1 a prikaz2 aj prázdne – v takom prípade sa na ich mieste nič nevykoná. Podobne môže byť prázdna aj podmienka podmienka, ktorá sa v takom prípade interpretuje ako true.

Nekonečný cyklus možno teda napísať aj ako

for ( ; true; ) {
    cout << "Hello, World!" << endl;
}

prípadne ako

for ( ; ; ) {
    cout << "Hello, World!" << endl;
}

Vnorené cykly

Vypíšeme tabuľku násobilky, v ktorej bude v riadku i a stĺpci j súčin i ⋅ j.

  • Použijeme dva vnorené cykly: jeden pôjde cez riadky, druhý cez stĺpce.
#include <iostream>
using namespace std;

int main() {
    int n;  // pokial ma ist nasobilka
    cin >> n;
    for (int riadok = 1; riadok <= n; riadok++) {
        for (int stlpec = 1; stlpec <= n; stlpec++) {
            cout << " " << riadok * stlpec;
        }
        cout << endl;
    }
}

Ukážka výstupu pre n=5:

 1 2 3 4 5
 2 4 6 8 10
 3 6 9 12 15
 4 8 12 16 20
 5 10 15 20 25

Ak pred každé číslo vypíšeme namiesto medzery " " tabulátor "\t", dostaneme krajší výstup

	1	2	3	4	5
	2	4	6	8	10
	3	6	9	12	15
	4	8	12	16	20
	5	10	15	20	25

Cvičenie: pridajme jednotlivým riadkom a stĺpcom hlavičku

Cykly: zhrnutie

  • Videli sme niekoľko príkladov využitia cyklov for a while.
  • Cyklus for je možné zapísať ako while (a naopak).
  • Z cyklu vieme vyskočiť príkazom break, prejsť na ďalšiu iteráciu príkazom continue. Používať s mierou.
  • Euklidov algoritmus rýchlo nájde najväčšieho spoločného deliteľa dvoch čísel.