Cvičenie 3 – Cykly, funkcie, referencie

Cieľom cvičení tretieho týždňa je precvičiť si:

  • využitie cyklov,
  • metódy vracajúce hodnoty,
  • debugovanie (krokovanie) programu.

Archetyp: jpaz2-archetype-quickstart

Na začiatok

  • Preskúmajte rôzne funkcie, ktoré poskytuje Math (nájdete ich tak, že do Google zadáte Math Java 8).
  • Čo nakreslí nižšie uvedená metóda mystery?

1
2
3
4
5
6
7
8
public void mystery() {
        for (int i=0; i<256; i++) {
                Color c = new Color(i, 100, 100);
                this.setPenColor(c);
                this.setPosition(i, 0);
                this.moveTo(i, 150);
        }
}

Nájdite v tejto metóde miesto, kde sa vytvorí objekt triedy Color. Koľko parametrov ma použitý konštruktor? Akého typu je premenná c?

  • Do triedy SmartTurtle pridajte metódu, ktorá nastaví náhodnú farbu kresliaceho pera.
  • Do triedy SmartTurtle pridajte metódu concentricCircles s parametrom radius, ktorá nakreslí „nekonečnú“ postupnosť vnorených sústredných kruhov. „Nekonečnú“ znamená, že útvary, ktoré nie je vidieť, už nekreslíme. Prvý (najväčší) kruh nech má polomer určený parametrom radius. Každý ďalší (vnorený) kruh, nech má polomer, ktorý je 80% z polomeru toho predošlého. Farby kruhov nech sa postupne cyklicky striedajú: červená, modrá, šedá.

1
2
3
public void concentricCircles(double radius) {

}
  • Upravte predošlú metódu tak, aby vrátila súčet obsahov skutočne nakreslených kruhov.

Korytnačie počty

  • Vytvorte triedu ScientificTurtle rozširujúcu triedu Turtle s nasledujúcimi metódami:
    • min vráti menšie z čísel cislo1 a cislo2
    • power vypočíta nk
    • factorial vypočíta n!
    • countDivisors vráti počet prirodzených deliteľov čísla n
    • isPrime vráti, či je číslo n prvočíslom
    • countDigits vráti počet cifier čísla n
    • containsDigit – vráti, či zápis čísla n obsahuje cifru c
    • hasNonincreasingDigits – vráti, či cifry čísla tvoria nerastúcu postupnosť. Cifry čísla 44220 tvoria nerastúcu postupnosť, ale cifry čísla 2231 nie (3 je väčšie ako naľavo od neho nachádzajúca sa cifra 2)
    • gcd vráti najväčšieho spoločného deliteľa čísel a a b (neskôr sa naučíme aj efektívny Euklidov algoritmus)
    • lcm vráti najmenší spoločný násobok čísel a a b

1
2
3
4
5
6
7
8
9
10
public double min(double cislo1, double cislo2)
public long power(int n, int k)
public long factorial(int n)
public int countDivisors(int n)
public int countDigits(int n)
public boolean isPrime(int n)
public boolean containsDigit(byte c, int n)
public boolean hasNonincreasingDigits(int n)
public int gcd(int a, int b)
public int lcm(int a, int b)
  • (Povinná úloha): odkrokujte výpočet niektorej z implementovaných metód.

Ďalšie úlohy

  • Naučte korytnačky triedy SmartTurtle metódu snooker, ktorá bude realizovať nasledovný algoritmus:
    • korytnačka príkazom this.setRangeStyle(RangeStyle.BOUNCE); nastaví, aby sa odrážala od hranice kresliaceho plátna
    • korytnačka na náhodných súradniciach nakreslí čierny kruh (dieru) s polomerom 10. X-ová aj Y-ová súradnica kruhu nech je náhodné číslo z intervalu <20, 280) (predpokladáme štandardnú kresliacu plochu s rozmermi 300 x 300).
    • kým sa korytnačka nedostane do vnútra vygenerovaného čierneho kruhu (diery) jej pohyb sa riadi týmito pravidlami:
      • každý krok korytnačky je krok aktuálnym smerom dĺžky 3 (za každým krokom pozastavíme program na pár milisekúnd napr. príkazom JPAZUtilities.delay(50))
      • každých 300 krokov sa korytnačka náhodne otočí

1
public void snooker()

Pre fajnšmekrov

  • Do triedy ScientificTurtle pridajte metódu findGreatestDigit, ktorá vráti najväčšiu cifru v zadanom čísle.
  • Kalkulačka sa nám pokazila a jediné operácie, ktoré dokáže zrealizovať sú pripočítanie jednotky k poslednému výsledku (číslu na displeji) a vynásobenie posledného výsledku dvomi. Na začiatku je na displeji číslo 0. Vytvorte metódu (v triede ScientificTurtle), ktorá pre zadané číslo vráti minimálny počet operácii, ktoré musíme vykonať, aby sme na displeji zobrazili parametrom zadané číslo n.
  • Pomocou obsahu prvých troch týždňov skúste zistiť odpoveď na otázku: „Aká je priemerná vzdialenosť dvoch bodov vo štvorci so stranou dĺžky jedna?“

Math.pow?

Math.pow je užitočná metóda na vyrátanie mocniny čísla s desatinnou čiarkou. Konkrétne Math.pow(a, b) vráti ab. Všimnime si však jej hlavičku:


1
double Math.pow(double a, double b)

Keďže exponent je typu double, túto metódu môžeme použiť aj na vypočítanie neceločíselných mocnín. Príklad: Math.pow(3.5, 0.25) vypočíta štvrtú odmocninu z čísla 3.5.

To, že táto metóda pracuje s číslami s desatinnou čiarkou, je tak jej silnou stránkou, ale pri nevhodnom použití aj slabou stránkou. Totiž čísla s desatinnou čiarkou majú limitovanú presnosť.

Príkladom nevhodného použitia tejto metódy je počítanie celých mocnín celých čísel. Vyskúšajte tento kód (napr. v metóde main triedy Launcher):


1
2
3
4
5
6
7
8
9
10
11
12
13
int a = 13;
int b = 15;

// Klasický výpočet násobením
long vysledok = 1;
for (int i = 0; i < b; i++) {
    vysledok = vysledok * a;
}
System.out.println(vysledok);

// Výpočet s využitím Math.pow a pretypovania
vysledok = (long) Math.pow(a, b);
System.out.println(vysledok);

Na doplnenie: 1315 = 51185893014090757, čo je menej ako maximálna hodnota pre long 9223372036854775807, t.j. nedôjde tu k pretečeniu (a rozdielnosť hodnôt tak nie je zapríčinená pretečením pri násobení ale limitovanou presnosťou hodnôt typu double).

Rýchlejší počet deliteľov

Cieľom jednej z úloh bolo pre zadané kladné nenulové číslo n určiť počet jeho deliteľov. Na prednáške bol prezentovaný jednoduchý algoritmus založený na testovaní všetkých celých čísel od 1 po n. Zároveň na prednáške bolo naznačené, že si vystačíme aj s testovaním menšieho počtu čísel – konkrétne √n čísel. Ako však na to?

Uvažujme číslo n. Nech a je nejakým deliteľom čísla n. Potom musí existovať také celé číslo b, že n = a.b. Teda číslo b je taktiež deliteľom čísla n. Na tieto 2 delitele čísla n môžeme pozerať ako na akési „kamarátske“ delitele čísla n. Bez ujmy na všeobecnosti nech a <= b. Ak a nie je √n, potom platí, že a < √n a b > √n (dôkaz sporom – ak by a < √n a b < √n, potom a.b < √n.√n = n, čo je v spore, že a.b = n; analogicky možno dokázať, že nenastane a > √n a b > √n). Dôsledkom tohto pozorovania je to, že ku každému deliteľovi ostro menšiemu ako √n prislúcha jeden deliteľ ostro väčší ako √n a naopak.

Zvyšok je už len programátorčina…