10. hét: számrendszerek, számábrázolás

Czirkos Zoltán · 2024.10.23.

A számrendszerekről szóló előadáshoz kapcsolódó feladatok. Néhány számábrázolási probléma megjelenése a programokban.

Felkészülés a laborra:

1. Negyedik kis ZH

A hétfői alkalmakon lesz a negyedik kis ZH.

2. Pót ZH

Jelentkeztél pót ZH-ra, ha írnod kell? Ha még nem, tedd meg most, már nem sok idő van hátra!

3. Operátorok és kiértékelés

Adjuk meg az alábbi kifejezésekhez tartozó kifejezésfát, figyelembe véve az operátorok precedenciáját!

  • 6 + 2 * 3
  • 2 * 6 - 5 / 3
  • a = b + c
  • t[i + 2] * 3
  • 5 * - 6
  • 5 - * 6

A legegyszerűbb ascii karakterekkel megrajzolni és úgy feltölteni. Ebben a feladatban nem kell kódolni, csak rajzolni.

Kétszer kettő:

  *
 / \
2   2
Megoldás
6+2*3
2*6-5/3
a=b+c

t[i+2]*3
5*-6
5-*6

4. 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!

print("1.", 1e200 / 1e-200)
print("2.", "igaz" if 1e3 + 1 == 1e3 else "hamis")
print("3.", "igaz" if 1e30 + 1 == 1e30 else "hamis")
Megoldás

Az 1-eshez: 10200/10-200 = 10400 lenne. De ez már nem fér bele a float típus ábrázolási tartományába, ezért végtelent kapunk.

Az 2-es és 3-as sorhoz: míg a 103+1 értéke különbözik 103-től, mivel a két szám, 1000 és 1 nem tér el túlzottan egymástól nagyságrendben. A 1030 és 1 esetén ez már nem igaz.

5. Hol a hiba?

Az alábbi program egy színátmenetes rajzot hivatott elkészíteni. A színek balról jobbra egyre pirosabbak, és alulról felfelé egyre kékebbek. A színkomponensek értéke mindig 0-tól 1-ig változik. Az osztások számát a program a felhasználótól kéri, az egyes kis négyzetek oldalhossza 30 képpont.

A program némely osztásszámra kifogástalanul működik, például db = 5-re a képen látható ábrát készíti. Más osztások esetén elromlik, db = 10 esetén szétcsúszik az ábra.

Mi a probléma? Magyarázd meg a jelenséget, és mutasd meg a nyomkövetőben is! Javítsd meg a programot, hogy minden osztásszámra helyesen működjön!

import turtle

def negyzet(a):
    turtle.begin_fill()
    for i in range(0, 4):
        turtle.forward(a)
        turtle.left(90)
    turtle.end_fill()

def main():
    a = 30  # oldalhossz
    db = int(input("Hány darabból? "))

    turtle.speed(0)
    b = 0.0
    while b <= 1.0:
        r = 0.0
        while r <= 1.0:
            turtle.fillcolor(r, 0, b)
            negyzet(a)
            turtle.forward(a)
            r += 1/(db-1)
        turtle.backward(db*a)
        turtle.left(90)
        turtle.forward(a)
        turtle.right(90)
        b += 1/(db-1)

    turtle.done()

main()
Megoldás

A lebegőpontos számítás pontatlansága. Az alábbi programocska jól mutatja a problémát db = 10 esetén:

db = 10

egesz = 0
valos = 0.0
while egesz <= db:
    print(egesz, valos)
    egesz += 1
    valos += 1 / (db-1)

Nem szabad float változóra építeni a ciklust, ha tudni akarjuk, hogy pontosan hányszor fut a törzse. Márpedig itt igen, mert pontosan 10 darab négyzetet kell rajzolni, nem 11-et vagy 9-et valamiféle kerekítési/számítási hiba miatt.

6. Kiírás adott számrendszerben I.

Írj programot, amelyik a megadott számot a megadott számrendszerbe alakítja át, és beteszi úgy egy sztringbe! Pl. a 9-es szám 2-es számrendszerben "1001". Az algoritmusod tetszőleges lehet, de a végén egy print(szam) utasítással ki kell tudni írni a számot, ahol a szam annak a változónak a neve, amiben az eredményt előállítottad.

Elég, ha előbb csak tízes számrendszerig működik a program! Ha működik jól a programod, akkor utána alakítsd át úgy, hogy nagyobb alap esetén is működjön! A 10-et, és annál nagyobb számjegyeket ilyenkor betűkkel szokás jelölni. Pl. 16-osban a 0…15 számjegyek: 012…89ABCDEF.

Tipp

Az alapötlet az, hogy maradékképzéssel látod a szám legutolsó számjegyét, utána pedig egész osztással le tudod vágni azt az utolsó számjegyet, amit kezeltél.

Szám%10//10
12344123
123312
1221
110
Megoldás

Az alsó számjegy meghatározása által mindig a legutolsó számjegyet kapjuk, vagyis fordított sorrendben állnak elő az adatok. A sorrend megfordítását sokféleképpen megoldhatjuk: például mindig a sztring elé fűzünk, esetleg mindig a végére, és a végeredményt fordítjuk meg.

7. Kiírás adott számrendszerben II.

Működik az előző feladatban megírt programod 0-ra is? Az algoritmustól függően előfordulhat, hogy 0-ra üres sztringet ad. Ha ilyen megoldást adtál, akkor egészítsd ki!

Oldd meg továbbá azt, hogy negatív számot is kaphasson! A pozitív számokat nem kell plusszal jelölni, a negatívak elé viszont kerüljön mínusz karakter!

8. Beolvasás adott számrendszerben

Írj programot, amely kap egy sztringet és egy számrendszernek a számát, majd megadja azt az egész számot, amit az adott sztringbeli számjegysorozat reprezentál! Például ha a felhasználó beírja, hogy 2-es számrendszer, és 1101, akkor ki kell írni, hogy 13. Először elég, ha tízes számrendszerig működik a program, csak utána írd át úgy, hogy működjön nagyobb alap esetén is!

Hányas számrendszerben fogsz írni?
16
Ird be a szamot!
fce2
A beolvasott szám 10-es számrendszerben: 64738

Ha elkészültél a saját algoritmussal, ellenőrizd különféle számokra a beépített konverzióval! Pl. int("fce2", 16) a fenti példa alapján 64738-at ad.

Tipp

Ehhez végig kell haladni a sztringen, és a meglévő adathoz (megszorozva persze az alappal) mindig hozzáadható az új érték:

Eddig megvoltÚj számjegyÚj érték
011
1212
123123
12341234
Megoldás

Megoldási terv, ötletek

  • Beolvasunk egy számjegyet (karaktert), és kivonjuk belőle a '0' karakterkódját. Így megkapjuk az értékét.
  • Hogy lesz egy számjegyből sok? Pl. 123 esetén, ha a 12-t már beolvastuk, és jön még egy 3-as, akkor a 12 igazából a 120-at jelentette (megszorozzuk 10-szel), és ahhoz adjuk a 3-at. Ha 123 esetén 4-es jön, akkor igazából 1230 volt, és ahhoz adjuk a 4-et.
  • Vagyis mindig az eddigi, szorozva 10-zel (a számrendszer alapjával), plusz az új számjegy.

Az egyszerű számításhoz felhasználhatjuk a Horner-elrendezést. Vegyük példának ehhez 10-es számrendszerben a 234-et! Ezt az egyes számjegyekből 2×102+3×101+4×100 formában határozhatjuk meg. Ami pedig ugyanaz, mint ((2×10)+3)×10+4, azaz (((0×10+2)×10)+3)×10+4, amiből már látszik, hogyan kell dolgoznunk: mindig a meglévő részeredményt megszoroznunk tízzel, és hozzáadni az új számjegy értékét. Ha eddig a 23-at láttuk, és megkapjuk a 4-est, akkor a szóban forgó lépés 23×10+4 = 230+4 lesz. Ezt kell folytatni egészen addig, amíg határoló karaktert nem kapunk, persze a tízzel szorzás helyett az adott számrendszer alapját tekintve.

A számjegy beolvasásánál figyelembe kell venni, hogy bár számjegyről beszélünk, tízes számrendszer fölött ez lehet betű is. Ezért a beolvasott karaktert meg kell vizsgálni, számjegyről van-e szó (0...9) vagy betűről (a...z). Ha számjegyről, akkor a '0'-s számjegy karakterkódját kell kivonni belőle, hogy számértéket kapjunk, amúgy pedig az 'a' betű karakterkódját, és hozzáadni 10-et, mert A=10, B=11 stb. Itt kapóra jönnek a sztring típus függvényei: .isdigit() = számjegy-e?, .isalpha() = betű-e?, .upper() = a karakter nagybetűként (ha szépen szeretnénk csinálni).

9. Konverziós függvények

Dolgozd át úgy az előző feladatok „kiírás adott számrendszerben” és „beolvasás adott számrendszerben” programkódjait, hogy azok függvények legyenek! Például:

  • szamrendszerbol("fce2", 16)64738.
  • szamrendszerbe(64738, 16)"fce2".

Készítsd fel a függvényeket hibák kezelésére! Pl. ha adott számrendszerben nem létezik egy számjegy, akkor dobjon kivételt az első függvény. Ha negatív számrendszerbe átalakítást kérünk, akkor dobjon kivételt a második, és így tovább. Milyen hibalehetőségek lehetnek még?

10. További feladatok

Ha a laborfeladatokkal elkészültél, dolgozz a példatárban lévő feladatokon, a szorgalmi feladatokon, a minta vizsgán, vagy a házi feladatodon.

Informatikusok próbálják lekódolni az Algoritmusok és gráfok tárgyon tanult algoritmusokat. Ezzel két legyet lehet ütni egy csapásra, mert két tantárgyat is gyakoroltok egyszerre.