6. labor: számrendszerek, bitműveletek
A laborok feladatai számrendszerekkel, és főként bitműveletekkel kapcsolatosak, de közben egyszerű függvényeket is kell írni.
1 Számrendszerek
hex | bin | dec |
---|---|---|
0 | 0000 | 0 |
1 | 0001 | 1 |
2 | 0010 | 2 |
… | … | … |
e | 1110 | 14 |
f | 1111 | 15 |
Váltsuk át a következő 2-es számrendszerbeli (bináris) számokat 10-esbe (decimális): 1011, 1101, 10000, 10101!
Váltsuk papíron át a következő decimális számokat binárisba: 13, 27, 35.
24=16, ezért minden hexadecimális (16-os számrendszerbeli) számjegynek pontosan 4 darab bináris számjegy felel meg, ahogyan az jobb oldalt is látható. Emiatt pl. a bináris 101011 hexadecimális értéke 0x2b, mivel alulról 4-es csoportokban 10'1011, és 10=0x2, illetve 1011=0xb. Váltsuk át a következő számokat hexadecimálisból bináris számrendszerbe: 0x1f, 0xfce2, 0xabba, 0xcaffe! Váltsuk át ezeket hexadecimálisba: 11111111, 1011100, 101, 10101!
2 Bitminta kiírása
Írj függvényt, amely paraméterként kap egy unsigned char
értéket, és kiírja az érték bitmintáját! Pl. 130 esetén 10000010-t.
Tételezd fel, hogy az unsigned char
típus 8 bites!
0x7f
, 0x80
, 0xff
,
0x55
számokat!
Megoldás
#include <stdio.h> /* Bitmintát kiíró függvény */ void bitminta(unsigned char c) { int i; /* Ciklus 7-0ig (hogy a legnagyobb helyiérték legyen az első) */ for (i=7; i>=0; i--) /* Minden lépésben elcsúsztatjuk i-vel a számot */ /* hogy az adott bitje kerüljön a legkisebb helyiértékre */ /* majd az &1-el levágunk mindent, kivéve a legkisebb helyiértéket */ printf("%u", (c>>i) & 1); } int main() { unsigned char i; for (i=0; i<20; i++) { bitminta(i); printf("\n"); } return 0; }
3 Bitek titkai
Az előző feladat folytatása: a bitenként kiíró függvényt írd át úgy, hogy unsigned int
típussal dolgozzon. Vagyis több bittel – most tételezzük fel, hogy az
unsigned int
32 bites. Másold be a kódba az alábbi tömböt, és írd ki külön sorba
az összes számot bitenként!
unsigned szamok[9]={0, 1934128004, 2219346506, 2219346513, 1685621663, 340306449, 3813175825, 2048, 0 };
Mit látsz? Írd át úgy, hogy a 0
-k helyett szóköz, az 1
-esek
helyett X
karakter jelenjen meg! (Ehhez használhatod a ?:
operátort is.)
Megoldás
#include <stdio.h> /* Bitmintát kiíró függvény */ void bitminta(unsigned c) { int i; /* Adott helyiértékű bit kiírása (szóköz vagy 'X') */ for (i=31; i>=0; i--) printf("%c", ((c>>i)&1) ? 'X' : ' '); } int main() { unsigned szamok[9]={ 0, 1934128004, 2219346506, 2219346513, 1685621663, 340306449, 3813175825, 2048, 0 }; int i; for (i=0; i<9; i++) { bitminta(szamok[i]); printf("\n"); } return 0; }
4 Bitbabrálás
Adott az alábbi függvény prototípus:
unsigned bitset(unsigned eredeti, unsigned bit);
- Valósítsd meg a függvényt, amely az
eredeti
paraméterben kapott számbit
sorszámú bitjét 1-re állítja, és visszatér az így kapott számmal! A többi bit maradjon változatlanul! A legkisebb helyiértékű bit száma 0. - Készíts
bitreset
függvényt is, melynek paraméterezése megegyezik abitset
-ével, és a megadott sorszámú bitet 0-ra állítja! - Végül pedig
bitnegal
függvényt, amely a megadott sorszámú bitet negálja.
Legyen unsigned c=51
! Végezd el a következő műveleteket, és írd ki egymás alá az egyes lépések eredményét!
Figyeld meg, hogyan változnak a bitek!
c=bitset(c, 5); c=bitreset(c, 7); c=bitnegal(c, 3);
Kipróbálhatod a függvényeket a „bitek titkai” feladat számain is. Egy adott sorszámú bitet billents mindegyik számban 1-re, és figyeld meg utána, hogyan néz ki az eredmény!
Megoldás
#include <stdio.h> unsigned bitset(unsigned eredeti, unsigned bit) { /* Veszünk egy 1-est (00...01) amit elshiftelünk az adott helyre */ /* majd a kapott értéket VAGY kapcsolatba hozzuk az eredetivel */ /* így az eredetinek az adott értékén biztos 1-es lesz */ /* a többi pedig nem változik */ return eredeti | (1<<bit); } unsigned bitreset(unsigned eredeti, unsigned bit) { /* Veszünk egy 1-est (00...01) amit elshiftelünk az adott helyre */ /* a kapott értéket negáljuk, így az adott helyiérték 0 lesz */ /* a többi pedig 1 (pl. 11110111) */ /* Ezt ÉS kapcsolatba hozzuk az eredetivel, így az eredeti adott bitje */ /* biztos 0 lesz, a többi pedig nem változik */ return eredeti & (~(1<<bit)); } unsigned bitnegal(unsigned eredeti, unsigned bit) { /* Veszünk egy 1-est (00...01) amit elshiftelünk az adott helyre */ /* majd a kapott értéket XOR kapcsolatba hozzuk az eredetivel */ /* így az eredetinek az adott értéke invertálódik */ /* a többi pedig nem változik */ return eredeti ^ (1<<bit); } /* Bitmintát kiíró függvény (az előző feladatból) */ void bitminta(unsigned c) { int i; /* Adott helyiértékű bit kiírása */ for (i=31; i>=0; i--) printf("%c", ((c>>i)&1) ? '1' : '0'); } int main() { unsigned c = 51; bitminta(c); printf("\n"); c=bitset(c, 5); bitminta(c); printf("\n"); c=bitreset(c, 7); bitminta(c); printf("\n"); c=bitnegal(c, 3); bitminta(c); printf("\n"); return 0; }
5 Hány bites az unsigned?
Az előző programokban feltételeztük, hogy a char
és
az int
8, illetve 32 bitesek. Nézzük meg, így van-e!
Írj programot, amely megszámolja, hogy hány bites egy
unsigned
típusú változó!
Tipp: állítsd be egy változó értékét úgy, hogy a legkisebb helyiértékű bitje 1-es legyen, az összes többi 0! Ezután indíts el egy ciklust, amely minden iterációban egy bittel balra tolja a változót! Amikor az 1-es bit eléri a változó bal szélét (azaz a legnagyobb helyiértékű bit lesz), a következő balra toláskor már nem fog beleférni a rendelkezésre álló helyre, elvész, vagyis a változó összes bitje 0 lesz. A ciklus tehát addig kell ismétlődjön, amíg a változó értéke nem nulla. Közben egy másik változóval számolni kell, hogy hány iteráció történt.
A HSZK számítógépein 32 kell legyen az eredmény.
Megoldás
#include <stdio.h> int main() { unsigned i=1; /* i=00.....01 */ int hossz=0; /* Hossz */ while (i!=0) { /* Amíg el nem tűnik a bal oldalon az 1-es */ i=i<<1; /* Addig shifteljük egyesével balra */ hossz++; /* És számoljuk, hogy hányszor shiftelődött */ } printf("unsigned hossza: %d bit\n", hossz); /* Eredmény */ return 0; }
6 Lebegőpontos
Mit írnak ki az alábbi program egyes sorai? Próbáld meg kitalálni! Futtasd le a programot, és magyarázd meg az eredményt!
#include <stdio.h> int main() { printf("1. %g\n", 1e30 / 1e-30); printf("2. %g\n", 1e30f / 1e-30f); printf("3. %g\n", 1e30); printf("4. %f\n", 1e30); printf("5. %s\n", 1e10+1 == 1e10 ? "igaz" : "hamis"); printf("6. %s\n", 1e30+1 == 1e30 ? "igaz" : "hamis"); return 0; }
A printf()
%g
formátumsztringe azt jelenti, hogy a normálalakú kiírást használja, ha az rövidebb: pl. 100000000 helyett 1e8.
Megoldás
1. 1e+60 2. inf 3. 1e+30 4. 1000000000000000019884624838656.000000 5. hamis 6. igaz
A 2-es sorban nem két double
, hanem két float
értéket osztunk el egymással.
Az eredmény már nagyobb, mint a legnagyobb, float
típus által ábrázolható szám, ezért a gép
végtelen nagynak veszi.
A 4-es sorban az látszik, hogy a 1030 értéket a gép nem tudja pontosan ábrázolni (mivel az kettes számrendszerben nem egész szám.)
Az 5-ös és 6-os sor hasonló az előző feladathoz: míg a 1010+1 értéke különbözik 1010-től, mivel a két szám nem tér el túlzottan egymástól nagyságrendben, a 1030+1 esetén ez már nem igaz.
Eltérő géptípusok és fordítók esetén az eredmények kicsit mások lehetnek.
7 Végtelen ciklus?
Az alábbi program egy olyan ciklust tartalmaz, mely addig fut, amíg egy
szám és egy nála eggyel nagyobb szám nem egyenlő egymással. Ha a
lebegőpontos típusaink végtelenül pontosak lennének, ez végtelen ciklust
eredményezne. Mi a helyzet a gyakorlatban? Próbáld ki float
és
double
típussal is!
#include <stdio.h> #include <stdlib.h> int main() { float e=0.0, f=1.0; while (e != f) { f *= 2.0; e = f+1; } printf("%f\n%f\n", e, f); return 0; }
8 További feladatok
Bitek cseréje
Írj egy függvényt, amely egy unsigned char
típusú változó
egymás melletti bitjeit cseréli meg! 7↔6, 5↔4 stb. Térjen vissza a függvény
az így feldolgozott számmal. A bitenként kiíró függvény segítségével
ellenőrizd az eredményt!
Megoldás
#include <stdio.h> /* Bitmintát kiíró függvény */ void bitminta(unsigned char c) { int i; /* Adott helyiértékű bit kiírása */ for (i=7; i>=0; i--) printf("%c", ((c>>i)&1) ? '1' : '0'); } /* Szomszádos bitek cseréje */ unsigned char bitcsere(unsigned char c) { int i; /* Végigmegyünk a biteken kettesével */ /* Kivesszük az i. és i+1. biteket */ /* Ha nem egyeznek nem egyeznek meg, akkor mindkettőt invertáljuk */ /* úgy, hogy egy 3-ast (00000011) a megfelelő helyre shiftelünk, */ /* majd XOR kapcsolatba hozzuk az eredetivel, így az adott 2 bit */ /* invertálódik */ /* Ha megegyeznek, akkor nem kell semmit csinálni */ for (i=0; i<8; i+=2) if (((c>>i)&1) != ((c>>(i+1))&1)) c = c ^ (3<<i); return c; } int main(){ unsigned char c=46; bitminta(c); printf("\n"); c=bitcsere(c); bitminta(c); printf("\n"); return 0; }
Eratoszthenész szitája spórolósan
Egy bájtban, amennyiben az 8 bites, 8 logikai
értéket lehet tárolni. Írd meg a gyakorlat „Eratoszthenész szitája”
feladatát úgy, hogy egy unsigned char
típusba tömörítse
8 egymás melletti szám prím/nem prím tulajdonságát – azaz csökkentsd
nyolcadára az ottani program memóriahasználatát.
Megoldás
#include <stdio.h> int main() { /* A tömb mérete */ enum { MERET=1000 }; unsigned char prim[MERET]; int i=0; /* uresen indulunk - mindent primszamnak tekintunk. */ /* a feladat itt minden bitet 1-be rakni. kis magia, hogy * nem szamonkent megyek, hanem inkabb charonkent. olyan * erteket rakok a charokba, ami csupa 1-bol all. */ for (i=0; i<MERET; i++) prim[i]=~0; /* ~0 - csupa egyesből allo */ /* megyunk a szitan - MERET*8-ig lehet! */ for (i=2; i<MERET*8; i++) /* es amit talalunk primet */ /* itt kiszedek egy bitet; a kifejezes erteke 0, vagy az * 1, 2, 4, 8... szamok valamelyike. Azok hasznalhatok * logikai erteknek: 0, ha nem prim! */ if (prim[i/8] & 1<<(i%8)) { int sz; printf("%8d", i); for (sz=i*2; sz<MERET*8; sz+=i) /* 0-ba allitom azt a bitet. */ prim[sz/8]=prim[sz/8] & ~(1<<(sz%8)); } printf("\n"); return 0; }
Néhány tudnivaló:
- Érdemes összehasonlítani a programot az előző óraival. Az algoritmus tökéletesen megegyezik, csak a tömbbel kapcsolatos műveletek bonyolódtak kicsit.
- Fontos, hogy a szabvány nem köti meg a
char
előjeles vagy előjel nélküli voltát. Ha számot tárolunk benne, akkor írjuk ki mindig, hogysigned
vagyunsigned
. - Amikor egy szám prím voltát teszteljük, akkor a tömbből egy adott bitet kell megvizsgálni. Ha összetett számként jelöljük meg, akkor az adott bitet 0-ba kell állítani.
- Egy
unsigned char
-ba 8 bitnyi információ fér. Ezért a tömböt[i/8]
-cal kell indexelni. A vizsgált bit sorszáma pedig ennek megfelelőeni%8
lesz, amely kifejezésnek az értéke 0..7 között változik.
Lottó 5-ös
Hányféleképpen lehet n valamiből kiválasztani k valamit? Ezt a
kombinatorikában kombinációnak nevezik („n
alatt a k
”). A lottóban 90 szám
van, és 5-öt kell választani; a biztos 5-ös találathoz
majdnem 44 millió szelvényt kell kitölteni:
90·89·88·87·86 ────────────── = 43 949 268 1·2·3·4·5
Feladat: írd meg a programot, amely kéri a felhasználótól n
és k
értékét. (A lottóban n=90
és k=5
.)
Ellenőrizd a program által adott eredményt! Vajon hibás a programod?
Kövesd a változók értékét a nyomkövetőben (különösen a számláló kiszámításánál),
és hasonlítsd össze azt a Számológép alkalmazásban kapottal!
Megoldás
/* A nem igazán működő megoldás */ #include <stdio.h> int main() { int n, k; int i, komb; printf("n="); scanf("%d", &n); printf("k="); scanf("%d", &k); /* 1, hogy ezt szorozgassuk tovabb */ komb=1; for (i=n; i>n-k; i--) komb=komb*i; /* es utana osztjuk a faktorialissal */ for (i=1; i<=k; i++) komb=komb/i; printf("Cnk=%d", komb); return 0; }
Miért helytelen az eredmény? Ellenőrizd
a nyomkövető segítségével a gép által végzett számítást. Miért ott
téveszti el, ahol? Az egyik fentebbi alapján, az unsigned
típus bitjei számának
ismeretében magyarázd meg az eredményt!
Hogyan lehetne javítani? Megoldható úgy is, ha maradunk az egész számoknál.
Figyeld meg: k=1
esetén a számláló csak 90
,
a nevező 1
. k=2
esetén a számláló 90·89
,
a nevező 1·2
. A nevező miatt osztunk kettővel, de a számlálóban
a két tényező közül az egyik biztosan páros, mert n·(n-1)
alakú.
Ugyanígy k=3
-nál a számlálóban van egy szám, amely biztosan
osztható 3-mal. Ha a ciklusban minden szorzás után rögtön az osztást is
elvégezzük, akkor nem kell tárolnunk a 90·89·88·87·86
művelet
eredményét, hanem végig csak kisebb számokat. Írd így is
meg a programot!
Megoldás
/* A fenti ötlettel javított megoldás */ #include <stdio.h> int main() { int n, k; int i, komb; printf("n="); scanf("%d", &n); printf("k="); scanf("%d", &k); komb=1; /* szorzunk es osztunk - lasd a magyarazatot */ for (i=1; i<=k; i++) { komb=komb*(n+1-i); komb=komb/i; } printf("Cnk=%d", komb); return 0; }
A másik lehetőség a javításra a valós típus használata.
Gyökkettő – a gyakorlatról ismerős feladat
A √2 számjegyei egymás után, sorban meghatározhatóak a következő módszerrel. Induljunk ki abból, hogy a gyöknek 1 és 2 között kell lennie, mivel 12=1 és 22=4. Az előbbi túl kicsi, az utóbbi már túl nagy, hogy a gyök lehessen. Menjünk tovább ugyanezzel a gondolattal, és határozzuk meg a tizedesvessző utáni első számjegyet:
Szám | Négyzet |
---|---|
1,0 | 1,00 |
1,1 | 1,21 |
1,2 | 1,44 |
1,3 | 1,69 |
1,4 | 1,96 |
1,5 | 2,25 |
Ebből tudjuk, hogy a gyök 1,4-gyel kezdődik. A következő számjegy:
Szám | Négyzet |
---|---|
1,40 | 1,9600 |
1,41 | 1,9881 |
1,42 | 2,0164 |
Vagyis 1,41 a keresett szám eleje, és így meg lehet határozni a többit is.
- Írj programot, amely a fenti algoritmussal 10−10 pontossággal meghatározza √2 értékét!
- Milyen típust kell ehhez használni? Meg tudod határozni a gyököt 10−20 pontossággal? Mi történik, ha megpróbálod, és miért?
- Hasonlítsd össze ezt az algoritmust Hérón módszerével. Vajon melyik gyorsabb? Melyik ad kevesebb lépésből pontosabb megoldást?
Megoldás
#include <stdio.h> #include <math.h> double gyok(double szam) { double tipp, novekmeny; unsigned lepesszam; tipp = 1; novekmeny = 1; lepesszam = 0; do { do { tipp += novekmeny; ++lepesszam; } while (szam > (tipp * tipp)); tipp -= novekmeny; novekmeny /= 10; } while (novekmeny > 1e-10); /* printf("Lepesszam: %d\n", lepesszam); */ return tipp; } int main() { printf("2 gyoke: %.10f\n", gyok(2)); return 0; }
Gyökkeresés
Tudjuk, hogy az x3-9x2+23x-15=0 egyenlet egy gyöke 2.2 és 4.5 között található. Írj programot, amely intervallumfelezéses módszerrel kiszámítja az egyenlet gyökét!
Tipp: Az intervallumfelezés módszere egy x_also és egy x_felso értékből indul ki, az ezekhez tartozó függvényértékekről tudjuk, hogy ellentétes előjelűek. Kiszámítjuk az x_kozepe=(x_also+x_felso)/2 értéket: ha az ehhez tartozó függvényérték előjele az x_also-hoz tartozó függvényérték előjelével egyezik meg, akkor x_also=x_kozepe, egyébként x_felso=x_kozepe. (Vagyis az x_also és x_felso távolságát felére csökkentjük úgy, hogy a gyök továbbra is a két határ között legyen.) Az eljárást addig folytatjuk, míg x_also és x_felső „elég közel” nem kerül egymáshoz (pl. epszilon=10-6). Ekkor a gyöknek x_also-t, x_felso-t, vagy az átlagukat tekinthetjük.
Ha sikerült kiszámítanod a gyököt, írd át a programot float
típusra (ha eddig nem az volt), és epszilont csökkentsd 10-8-ra (C nyelven 1e-8). Mit tapasztalsz?
(x-1)(x-10n)=0
Írj függvényt, amely megoldja az (x-1)(x-10n)=0 egyenletet!
Ehhez alakítsd át az egyenletet x2-(1+10n)x+10n=0
alakba és az együtthatókat helyettesítsd be a megoldóképletbe. A függvény bemeneti paramétere n legyen.
Próbáld ki a függvényt n = 1, 2, 4, 8 esetekre és float
valamint double
típusokkal is! Figyeld meg, hogy mi történik és adj rá magyarázatot!
Megoldás
#include <stdio.h> #include <math.h> void megold(int n) { float b,c; /* double b,c; */ int i; /* 10 hatvány kiszámolása */ c=1; for(i=0;i<n;i++) c*=10; b=-(c+1); /* Megoldások kiszámolása/kiírása */ printf("x1=%f x2=%f\n",(-b+sqrt(b*b-4*c))/2,(-b-sqrt(b*b-4*c))/2); } int main() { /* Próba... */ megold(1); megold(2); megold(4); megold(8); return 0; }
Sakktábla
Írj függvényeket, amelyek paraméterei két koordinátapár, amelyek egy mezőre hivatkoznak a sakktáblán! (Ez lehet négy karakter is, pl. d6 és e8.) Az egyes függvények mondják meg, hogy az adott mezőpár helyes lépés-e egy királynak, bástyának, futónak, huszárnak vagy vezérnek.
Írj programot, amely megkérdezi egy kiinduló mezőnek a koordinátáit a felhasználótól, és aztán kilistázza az egyes figurák által elérhető mezőket!
A függvények segítségével „sormintamentessé” tehető a program. Hasonlítsd össze az így kapott programot a negyedik labor anyagában található megoldással. Miben segítenek még a függvények?
Megoldás
#include <stdio.h> #include <stdlib.h> /* a betűkre karakterként (számként) tekintek. * mivel ábécé sorban vannak, a számjegyek pedig növekvő * sorrendben, ezért kisebb/nagyobb összehasonlítást * végezhetek, és kivonhatom őket egymásból. */ /* igazat ad vissza, ha a megadott koordinatak nem * egy helyes mezot adnak (a1->h8). */ int helytelen_mezo(char o, char s) { return o<'a' || o>'h' || s<'0' || s>'8'; } /* kivételes eset mindegyik figuránál, ha nem lépett * sehova (o1==o2 és s1==s2), hiszen az nem is lépés. * ha a két kapott koordinátapár ilyen, a függvény * igazzal tér vissza. */ int mozdulatlan(char o1, char s1, char o2, char s2) { return o1==o2 && s1==s2; } /* a király egyet léphet valamelyik irányba. ez * azt jelenti, hogy a sor- és az oszlopugrás * távolságának abszolút értéke maximum egy, de * az nem helyes lépés számára, ha mozdulatlan marad. */ int kiralynak(char o1, char s1, char o2, char s2) { return abs(o1-o2)<=1 && abs(s1-s2)<=1 && !mozdulatlan(o1, o2, s1, s2); } /* a ló nehéznek tűnik, de nem az. az L alak * azt jelenti, hogy a vízszintes elmozdulás 1, * a függőleges 2, vagy fordítva. itt a képlet * kizárja a mozdulatlanságot. */ int huszarnak(char o1, char s1, char o2, char s2) { return (abs(o1-o2)==2 && abs(s1-s2)==1) || (abs(o1-o2)==1 && abs(s1-s2)==2); } /* a bástyánál az oszlop- vagy a sor változatlan. * de mindkettő nem lehet ugyanaz, azaz nem lehet * mozdulatlan a figura, mert az nem lépés. */ int bastyanak(char o1, char s1, char o2, char s2) { return (o1==o2 || s1==s2) && !mozdulatlan(o1, s1, o2, s2); } /* a futónál mindkét irányba ugyanannyit kell * mozdulni, úgy jön ki az átlós lépés. */ int futonak(char o1, char s1, char o2, char s2) { return abs(o1-o2)==abs(s1-s2) && !mozdulatlan(o1, s1, o2, s2); } /* a királynő mint a bástya és a futó együtt. */ int vezernek(char o1, char s1, char o2, char s2) { return bastyanak(o1, s1, o2, s2) || futonak(o1, s1, o2, s2); } int main() { /* a kapott mezők koordinátái */ char o1, s1, o2, s2; /* megkérdezzük a felhasználót. */ /* a scanf-nél a szóközök elnyelik a whitespace karaktereket. */ printf("Írd be az első mezőt, pl. d6!\n? "); scanf(" %c %c", &o1, &s1); printf("Írd be a második mezőt, pl. f8!\n? "); scanf(" %c %c", &o2, &s2); if (helytelen_mezo(o1, s1) || helytelen_mezo(o2, s2)) { printf("Hibás sor- vagy oszlopmegadás!\n"); } else { if (kiralynak(o1, s1, o2, s2)) { printf("Ez szabályos a király számára.\n"); } if (huszarnak(o1, s1, o2, s2)) { printf("Huszár számára szabályos.\n"); } if (bastyanak(o1, s1, o2, s2)) { printf("Egy bástya léphet így.\n"); } if (futonak(o1, s1, o2, s2)) { printf("Egy futó számára ez helyes lépés lehet.\n"); } if (vezernek(o1, s1, o2, s2)) { printf("A vezér léphet ilyet.\n"); } } return 0; }
Számkitaláló
Készítsünk egy számkitaláló programot! A program kitalál véletlenszerűen egy pozitív egész számot (1 és 1000 között), a felhasználó pedig addig tippel, amíg meg nem találja a keresett számot. A program minden tipp után megmondja, hogy a felhasználó tippje kisebb vagy nagyobb a keresett értéknél. Ha eltalálta, akkor pedig azt. Ilyenkor egyúttal be is fejeződik a program futása.
Megoldás
A feladat megoldása nagyon jó példa a hátultesztelő ciklus alkalmazására. Minimum egy tippet kérnünk kell – a ciklusmag, amely a tippet kéri, és a beírt számot ellenőrzi, egyszer legalább lefut. Illetve a gép minimum egy számot kitalál, és utána várja a felhasználótól a megfejtést.
A belső ciklusmagban az egyenlőséget nem is kell ellenőrizni, mert azt a ciklus feltétele megteszi. Ha egyenlő a tipp a gondolt számmal, akkor kijövünk a ciklusból, és ott viszont gondolkodás nélkül ki lehet írni, hogy talált.
#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { char meg; /* Generator inicializalasa. */ srand(time(0)); do { int gondolt, tipp; gondolt=rand()%1000+1; printf("Gondoltam egy szamot 1 es 1000 kozott. Talald ki!\n"); do { printf("Mi a tipped? "); scanf("%d", &tipp); if (gondolt>tipp) printf("Nagyobbra gondoltam!\n"); if (gondolt<tipp) printf("Kisebbre gondoltam!\n"); } while (tipp!=gondolt); printf("Gratulalok, kitalaltad! A gondolt szam %d.\n", gondolt); printf("Akarsz meg jatszani (i/n)? "); scanf(" %c", &meg); } while (meg=='i' || meg=='I'); return 0; }
scanf()
-guruknak: a %c
előtti szóköz azt jelenti,
hogy a bemeneten eldobjuk a whitespace karaktereket. A %c
beolvassa
azt is, egyébként semmi más nem. Erre azért van szükség, mert a legutolsó
tipp utáni entert az előző scanf
még a bemeneten hagyta.
Mi a nyerő stratégia a gép „ellen”? Hogyan lehet legkevesebb tippből kitalálni a számot, amire a gép gondolt? A program megírható fordítva is: a felhasználó gondolja ki a számot, és a gép találja ki azt a kapott kisebb/nagyobb válaszok alapján.
3D vektorok
Az előadáson bemutatott törtes példa alapján írjunk egy programot, amelyik háromdimenziós vektor típust képes kezelni! Tudjon vektorokat kiírni, összeadni, kivonni; számítsa ki két vektor skaláris szorzatát! A program kerete az alábbi legyen:
#include <stdio.h> /* ... a megírt programrészek ... */ int main() { Vektor a={3, 2, 1}, b={4, 6, 8}, c; c=osszead(a, b); kiir(c); printf("\n"); c=kivon(a, b); kiir(c); printf("\n"); printf("Skalárszorzat: %g\n", skalarszorzat(a, b)); return 0; }
Megoldás
Egy háromdimenziós vektor x, y, és z komponensekből áll. Ezeket egy struktúrába tehetjük, mivel összetartozó értékek, elválaszthatatlanok egymástól, és együtt adnak ki egy vektort.
typedef struct Vektor { double x, y, z; } Vektor; /* kiirja egy vektor komponenseit */ void kiir(Vektor v) { printf("(%g;%g;%g)", v.x, v.y, v.z); } /* osszead ket vektort, visszater az osszeggel */ Vektor osszead(Vektor a, Vektor b) { Vektor ossz; /* a kivon()-hoz hasonlóan is lehetne */ ossz.x=a.x+b.x; ossz.y=a.y+b.y; ossz.z=a.z+b.z; return ossz; } /* kivon ket vektort, visszater a kulonbseggel */ Vektor kivon(Vektor a, Vektor b) { /* az osszead()-hoz hasonloan is lehetne */ Vektor eredm={a.x-b.x, a.y-b.y, a.z-b.z}; return eredm; } /* visszater a ket vektor skalarszorzataval */ double skalarszorzat(Vektor a, Vektor b) { return a.x*b.x + a.y*b.y + a.z*b.z; }