Gyakorlati alapok
A ló gikája avagy a döntés logikája (páros vagy páratlan)
Ez az első fejezet, amelyben előszedjük a számítógép egyik óriási előnyét: nemcsak számol szélvész sebességben, hanem logikai döntéseket is képes hozni. Most induljunk ki abból: a programnak el kell döntenie bizonyos számokról, hogy azok párosak vagy páratlanok-e.
A döntés megvalósítása nyilvánvalóan ebben az esetben matematikán alapul, hiszen ha (if) páros számot 2-vel osztok, akkor az osztás maradéka mindig 0, másként (else) a szám páratlan lesz.
Nézzük meg futtatható Java-kódját, amely a páros számokat listázza ki 1 és 50 között. Ehhez a legegyszerűbb megoldás for ciklust használnunk:
public class Main {
public static void main(String[] args) {
for (int i = 1; i < 50; i++){
if (i % 2 ==
0){
System.out.print(i + " ");
}
}
}
}
Végeredmény:
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48
A kód kulcseleme a feltétel megadása, itt i % 2 == 0.
Emlékezzünk vissza az Aritmetikai operátorok című fejezetből, hogy a % jel a Javában nem a százalék, hanem a maradékképzés operátora, a fenti feltételmegadás tehát azt állítja:
i % 2 == 0
ha a számot 2-vel osztottuk és annak maradéka egyenlő 0-val
...akkor legyen valamilyen művelet (itt kiírás).
Páratlan számok keresésekor a feltételt kell "tagadni", azaz az ellenkezőjére állítani...
ha a számot 2-vel osztottuk és annak maradéka NEM egyenlő 0-val
...amelyet Javában így jelölünk:
i % 2 != 0
public class Main {
public static void main(String[] args) {
for (int i = 1; i < 50; i++){
if (i % 2 !=
0){
System.out.print(i + " ");
}
}
}
}
Végeredmény:
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49
Vagy:
public class Main {
public static void main(String[] args) {
for (int i = 1; i < 50; i++){
if (i % 2 ==
1){
System.out.print(i + " ");
}
}
}
}
Végeredmény:
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49
Most készítsünk egy olyan algoritmust, amelyik leválogatja, külön-külön eltárolja, majd kiírja a páros és páratlan számokat! A tároláshoz, majd a leválogatott számok külön sorokba való kiírásához 2 db statikus méretű tömböt fogunk felhasználni. A tömbök deklarációjánál betartjuk a programozási névkonvenciókat (erről szól az Adatok, metódusok elnevezésének problémái című fejezet):
-
tombParos
-
tombParatlan
public class Main {
public static void main(String[] args) {
int[] tombParos = new int[50];
int[] tombParatlan = new int[50];
for (int i = 0; i < 50; i++){
if (i % 2 ==
0){
tombParos[i] = i;
}
else
tombParatlan[i] = i;
}
for(int i = 0; i < tombParos.length;
i++){
System.out.print(tombParos[i] + " ");
}
System.out.println();
for(int i = 0; i <
tombParatlan.length; i++){
System.out.print(tombParatlan[i] + " ");
}
}
}
Végeredmény:
0 0 2 0 4 0 6 0 8 0 10 0 12 0 14 0 16 0 18 0 20
0 22 0 24 0 26 0 28 0 30 0 32 0 34 0 36 0 38 0 40 0 42 0 44 0 46 0 48 0 50
0 1 0 3 0 5 0 7 0 9 0 11 0 13 0 15 0 17 0 19 0 21 0 23 0 25 0 27 0 29 0 31 0
33 0 35 0 37 0 39 0 41 0 43 0 45 0 47 0 49 0
A végeredmény számunkra nem éppen megfelelő, mert mindkét tömb tele lett 0-val.
Fontos tudnunk, hogy a tömbök létrehozásának pillanatában...
int tombParos[] = new int[50];
int tombParatlan[] = new int[50];
...a 2 tömb minden egyes eleme 0-val fel is lett töltve, azaz inicializálva.
Ebből következően a főciklusban lévő feltételvizsgálat vagy ide vagy oda pakolta az értékeket, ahová pedig nem jutott, ott 0 maradt. A részleges megoldás, hogy 2 külön ciklusban kell megtennünk a feltételvizsgálatot, ám akkor már ki is írjuk az értékeket:
public class Main {
public static void main(String[] args) {
int tombParos[] = new int[50];
int tombParatlan[] = new int[50];
for(int i = 0; i < 50; i++){
if(i % 2 ==
0){
tombParos[i] = i;
System.out.print(tombParos[i] + " ");
}
}
System.out.println();
for(int i = 0; i < 50; i++){
if(i % 2 !=
0){
tombParatlan[i] = i;
System.out.print(tombParatlan[i] + " ");
}
}
}
}
Végeredmény:
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
40 42 44 46 48
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49
A következő probléma megoldása már nehéz lesz!
Adott egy valós probléma: túl nagyok a tömbök méretei.
A tömbök még mindig sok 0-t tartalmaznak, csak nem lettek kiírva. Valójában nekünk pontosan fele ekkora nagyságú tömbökre van szükségünk, hiszen 0-tól 50-ig 25 db páros és 25 db páratlan számunk van.
A fenti kód tehát nem teljesít egy fontos alapkövetelményt: mindenkori takarékosságot a műveleti memóriával és kódméretekkel. Ezt erősíti az, ha kiírjuk például a páratlan tömb aktuális tartalmát:
for(int i = 0; i < 50; i++){
System.out.print(tombParatlan[i] + " ");
}
Végeredmény:
0 1 0 3 0 5 0 7 0 9 0 11 0 13 0 15 0 17 0 19 0 21 0 23 0 25 0 27 0 29 0 31 0 33 0 35 0 37 0 39 0 41 0 43 0 45 0 47 0 49
A megoldáshoz be kell vezetnünk egy segédváltozót (itt int szamlalo). A segédváltozók, amelyeket szokták flag-eknek is nevezni, valamilyen állapotváltozás ideiglenes tárolására használatosak. Ezek típusuk és lehetőségük szerint rendkívül sokfélék lehetnek; alapszintű működésük Az állapotjelzők (flag) című fejezetben tanulmányozhatók.
Először nézzük meg az alábbi futtatható Java-kódot, majd alatta szétboncoljuk működését:
public class Main {
public static void main(String[] args) {
int tombParos[] = new int[25];
int tombParatlan[] = new int[25];
for(int i = 0; i < 50; i++){
if(i % 2 == 0){
int szamlalo
= 0;
tombParos[szamlalo] = i;
System.out.print(tombParos[szamlalo] + " ");
szamlalo++;
}
}
System.out.println();
for(int i = 0; i < 50; i++){
if(i % 2 != 0){
int szamlalo
= 0;
tombParatlan[szamlalo] = i;
System.out.print(tombParatlan[szamlalo] + " ");
szamlalo++;
}
}
}
}
Végeredmény:
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
40 42 44 46 48
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49
A végeredmény nem változott, ám a tömbök feleakkora nagyságúak lettek:
int tombParos[] = new int[25];
int tombParatlan[] = new int[25];
Ez volt a fő cél. Ennek eléréséhez le kellett válnunk a fő ciklus i léptetéséről és el kellett érnünk, hogy a tömbindex (tomb[i]) csakis a feltétel teljesülésekor lépjen. Ezt már említett módon egy segédváltozó (szamlalo) bevezetésével értük el, amelyet kizárólag a cikluson belül léptettünk (szamlalo++).
Ugyanakkor azt is fontos észrevennünk, hogy a szamlalo csak egy lokális, azaz rövid életű változó: élettartama pontosan a for ciklus élettartamával egyenlő.
Újabb ötlet: vajon ezzel a módszerrel tudjuk-e csökkenteni a for ciklusok számát, ezzel kis processzoridőt is megtakarítva?
public class Main {
public static void main(String[] args) {
int tombParos[] = new int[25];
int tombParatlan[] = new int[25];
for(int i = 0; i < 50; i++){
if(i % 2 == 0){
int
szamlaloParos = 0;
tombParos[szamlaloParos] = i;
szamlaloParos++;
}
else {
int
szamlaloParatlan = 0;
tombParos[szamlaloParatlan] = i;
szamlaloParatlan++;
}
}
}
}
A szamlaloParatlan bevezetésével már csak 1 fő rendezőciklusunk van, ám tömbelemkiíráshoz mindenképpen fel kell használnunk további for ciklusokat.
Házi feladat - A fenti vagy ahhoz hasonló módszerrel töltsünk fel egy 10 elemből álló tömböt páros számokkal! Több megoldás is lehetséges!
A megoldás fejezete a képre kattintva érhető el.