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;
}
