4. gyakorlat: programozási tételek és tömbök
Ez a gyakorlati óra a címben említett két témakört érinti. Az egyes feladatoknál gondoljuk át a következőket:
- Melyik programozási tételt (számlálás, lineáris keresés stb.) alkalmazzuk, és miért?
- Tömböknél: mit tárolunk és miért? Hogyan szervezzük a tárolást? Mekkora a tömb mérete, mi a legalsó és legfelső tömbindex jelentése?
1 Osztók összege
Adjuk meg egy felhasználótól kért szám osztóinak összegét! (Pl. 6-hoz: 1+2+3+6 = 12.) Melyik programozási tételeket kell ehhez kombinálni? Nevezzük meg őket! Írjuk meg a programot úgy is, hogy az osztók összegébe a számot önmagát nem számítjuk bele! Hol kell ehhez módosítani a programot?
Tökéletes szám az, amelynél az utóbbi összeg (vagyis az osztók 1-et beleértve, de a számot magát nem) megegyezik magával a számmal. A 6 a legkisebb tökéletes szám (1+2+3=6). A következők 28 és 496. Írjuk ki, hogy a kapott szám tökéletes-e!
Megoldás
Az osztók összegzéséről egyből eszünkbe juthat az összegzés tétele: ciklus a számokon, akkumulátor változóban összegzés. Az összeghez azonban nem mindegyik számot kell hozzáadni, hanem csak az osztókat, vagyis válogatunk közülük. A kiválogatás tétele haszonlít a számláláshoz: ha egy feltétel teljesül, akkor csinálunk valamit a számmal:
CIKLUS AMÍG van még szám, ADDIG szám = következő elem HA feltétel(szám), AKKOR KIÍR: szám FELTÉTEL VÉGE CIKLUS VÉGE
#include <stdio.h> int main() { int szam, oszto, osztoosszeg; printf("Szam: "); scanf("%d", &szam); osztoosszeg=0; /* oszto>szam/2 mar nincsen (csak sajat maga lenne) */ for (oszto=1; oszto<=szam/2; oszto+=1) if (szam%oszto==0) osztoosszeg+=oszto; if (osztoosszeg==szam) printf("Ez egy tokeletes szam.\n"); else printf("Nem tokeletes szam, %d != %d.\n", szam, osztoosszeg); return 0; }
2 Az év napja
Írjunk programot, amelyik egy adott dátumról (év, hónap, nap) kiírja, hogy az év hányadik napja! Az év paraméterre a szökőévek miatt van szükség. Szökőév minden negyedik, nem szökőév minden századik, de szökőév minden 400-adik. 2000-ben ezért volt szökőév.
Megoldás
#include <stdio.h> int main() { /* a program altal hasznalt tablazat */ int honapok[]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* ehhez fogja kiszamolni a napot */ int ev=2013, honap=2, nap=4; /* segedvaltozok */ int szokoev, hanyadik, i; /* az adott honapnal mar nem kell hozzaadni a napok szamat, ezert i<honap. A tomb viszont 0-tol szamozodik, ezert kivonok az indexbol mindig 1-et! Ha a januar napjait adom hozza epp, akkor a 0-s indexu elem kell. */ hanyadik=0; for (i=1; i<honap; i+=1) hanyadik+=honapok[i-1]; /* akkor szokoev, ha oszthato 400-zal VAGY (oszthato 4-gyel ES nem oszthato 100-zal). ha szokoev van, es a februar mar eltelt, akkor +1 nap (mert csak 28-at adtunk hozza, de 29-et kellene */ szokoev=ev%400==0 || (ev%100!=0 && ev%4==0); if (szokoev && honap>2) hanyadik+=1; /* vegul a mostani honapbol eltelt napok */ hanyadik+=nap; printf("%4d.%02d.%02d. az ev %d. napja.\n", ev, honap, nap, hanyadik); return 0; }
Figyelni kell itt, hogy mindig a hónap-1 indexet használjuk a tömbön, mivel a január, az 1. hónap adata a 0. indexű elemben van. Ezt úgy is meg lehetne oldani, ha a 0. indexű elembe betennénk egy helytartót, pl. egy 0 értéket. Mert akkor a januárhoz tartozó 31-es szám az 1. indexre kerülne, a február 28-a a 2. indexre stb.
int honapok[]={0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
3 Statisztika a számokról
Írjunk programot, amelyik a szabványos bemenetről olvas, fájl vége jelig. Számolja meg, hogy a beírt számok közül, amelyek 1 és 10 között vannak, melyik hányszor szerepelt! Írja ki ezt a szabványos kimenetre a következő formában:
1: 5 db 2: 3 db 3: 4 db ...
Megoldás
Az egyes számjegyeket, mivel tudjuk, hogy összesen pontosan 10 van belőlük, egy tömbben lehet tárolni. A bemenet végigolvasására egy ciklust írunk. Itt a számlálás tételét kell megvalósítani, csak 10 darab számlálóval. Hogy melyik számlálóról van szó, azt a tömb indexelésével választjuk ki. Mivel a beérkező számok az 1…10 tartományban vannak, a tömb viszont 0…9 tartományban indexelődik, ezért az indexeléshez mindig a szám−1 értéket használjuk. (Kényelmi okokból, hogy mindenhol ugyanilyen legyen az indexelés, a számlálók nullázása és a kiírás is ezt az elvet követi a programban.)
#include <stdio.h> int main() { int t[10], i; int c; for (i=1; i<=10; i+=1) /* nullazas */ t[i-1]=0; printf("Irj be 1 es 10 kozotti szamokat!\n"); /* amíg sikerül számot beolvasni */ while (scanf("%d", &c)==1) { /* feldolgozas */ if (c>=1 && c<=10) t[c-1]+=1; else printf("1 es 10 kozotti szamokat adj meg!\n"); } for (i=1; i<=10; i+=1) /* kiiras */ printf("%d: %d\n", i, t[i-1]); return 0; }
A scanf()
a beolvasott számokon kívül is ad egy számot vissza. Ezt ellenőrizve
megtudhatjuk, hogy hány konverzió sikerült. A lenti kódban egy konverzió van (%d
,
egy szám); ha az sikerül, akkor a scanf()
értéke 1 lesz.
Vagyis a ciklus addig fut, amíg van beolvasott szám. Fájl vége jelnél a kapott
érték nem lesz egy, mivel nincs már beolvasott szám. („Fájl vége jel” – az első laboron szerepelt!)
4 Bankautomata
Pénzvisszaadós automatába kell egy olyan programrészt írnunk, amelyik a visszajárót számolja ki.
Írjunk egy programrészt, amely egy adott pénzösszegről kiírja,
hogy hogyan lehet azt a legkevesebb papírdarabbal/fémkoronggal kiadni!
Például: 1415 Ft = 1000 Ft + 2×200 Ft + 10 Ft + 5 Ft.
Megoldás
#include <stdio.h> int main() { /* nullaval jelzem a tomb veget */ int penzek[]={20000, 10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 0}; int mennyit, i; printf("Mennyi a visszajaro? "); scanf("%d", &mennyit); printf("Az automata kiadja:\n"); /* feltetelezzuk, hogy a tombben csokkeno sorrendben vannak benne a bankjegyek. eloszor a legnagyobbol probalunk adni. */ for (i=0; penzek[i]!=0; i+=1) { if (mennyit>=penzek[i]) { int db=mennyit/penzek[i]; printf("%d db %d Ft-os.\n", db, penzek[i]); /* a darabot visszaszorozva megvan az osszeg, amit kiadtunk ebben a lepesben */ mennyit-=db*penzek[i]; } } if (mennyit!=0) printf("Nincs mar ilyen kicsi erme: %d Ft\n", mennyit); return 0; }
A megoldásban csökkenő sorrendben vizsgáljuk a címleteket, és mindegyikből kiadunk annyit, amennyit lehet. A csökkenő sorrendet úgy állítjuk elő, hogy a tömböt, amely a címleteket tartalmazza, eleve csökkenő sorrendbe rendezve építjük be a programba. Ez egy ún. mohó algoritmus – mindig a legnagyobbat próbálja lépni a megoldás felé.
Egy tömbről általában nekünk kell megjegyeznünk a méretét, vagyis
hogy hány elemet tartalmaz. Itt azonban lehet kicsit trükközni. A tömb
utolsó eleme egy nullás szám, az jelöli meg a végét. A ciklus futási
feltétele penzek[i]!=0
, amelyben nem szerepel a tömb
mérete. Azért tudjuk ezt megtenni, mert a nulla ebben a tömbben
értelmetlen adat: nincs nulla forintos bankjegy. Ugyanígy a −1 is jó
lenne, vagy bármi, amit az értékes adatoktól meg tudunk
különböztetni.
A mennyit/penzek[i]
kifejezés értéke egy egész szám,
pl. 2100/1000=2. Az egész/egész osztás C-ben egész számot eredményez! Ezt
ki is használjuk a programban.
5 Eratoszthenész szitája
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Eratoszthenész szitája prímszámokat keres. A módszer a következő. Felírjuk a számokat valameddig. 2 prímszám, ezt megjegyezzük. Kihúzzuk a többszöröseit, mivel azok nem prímszámok. Ezután 3 a következő, ami még nincs kihúzva. Az is prímszám, mivel nem találtunk ezidáig osztót hozzá: a nála kisebb összes szám többszöröseit kihúztuk, nála nagyobb osztója pedig nem lehet. A többszörösei viszont nem prímek: kihúzzuk az összes 3-mal oszthatót. 4-et már kihúztuk (2×2). 5 a következő prím, kihúzzuk n×5-öt stb. Írjuk ki ez alapján a prímszámokat 999-ig!
Megoldás
A tömb elemei itt logikai értékek.
A tömböt pedig mindig magával a számmal indexeljük; prim[2]
pl. azt tárolja, hogy
a 2 prímszám-e. Az első két elemet így ugyan nem használjuk, de a program egyszerűbb, mert
a tömbindex megegyezik magával a számmal. A prim[1000]
méretű tömbben így 999-ig lehet
megkeresni a prímeket.
#include <stdio.h> int main() { int prim[1000]; int i; /* uresen indulunk - mindent primszamnak tekintunk */ for (i=0; i<1000; i+=1) prim[i]=1; for (i=2; i<1000; i+=1) /* megyunk a szitan */ if (prim[i]) { /* es amit talalunk primet */ int szam; printf("%d ", i); for (szam=i*2; szam<1000; szam+=i) prim[szam]=0; } printf("\n"); return 0; }
A többszörösök kihúzására ilyen ciklus is elképzelhető:
int szorzo; szorzo=2; while (i*szorzo<1000) { prim[i*szorzo]=0; /* a tobbszorosei nem primek. */ szorzo+=1; }