Gyakorlat, 3. hét: egyszerű programok

Czirkos Zoltán · 2023.09.19.

Egyszerű C programok és vezérlési szerkezeteik. Számok beolvasása és kiírása.

Ezen a héten már C programokat írunk. Felkészülés a gyakorlatra:

1. Próba ZH

A feladat segít felmérni, hogy állsz a tanulással. A szövege csak a többi megoldással együtt jelenik meg. Óra után pedig rögzítsd a pontszámod az admin portálon – a dolgozat nálad marad.

Feladat

Írj C programot, amely kér a felhasználótól egy számot. Megvizsgálja, majd kiírja, hogy prímszám-e.

Kérem a számot: 6
Nem prím.

A feladat megoldására 10 perc áll rendelkezésre.

Mintamegoldás és pontozási útmutató
#include <stdio.h>
#include <stdbool.h>
int main(void) {
    int szam;
    printf("Kérem a számot: ");
    scanf("%d", &szam);
    
    bool vanoszto = false;
    int oszto = 2;
    while (oszto < szam && !vanoszto) {
        if (szam % oszto == 0)
            vanoszto = true;
        oszto += 1;
    }
    
    if (vanoszto)
        printf("Nem prím.\n");
    else
        printf("Prím.\n");

    return 0;
}
Helyesség:
1p  Program kerete: include (legalább stdio.h), int main, 
    return 0 (ez utóbbi elmaradhat).
    Csak akkor jár, ha a feladatot oldja meg, pl. "helló világ"-ra nem.
1p  Adat bekérése, scanf helyes használata (& ne maradjon le!)
1p  Eldöntés eredményéhez kell egy logikai változó, értelmes kezdőértékkel.
1p  Egész osztó változó (for-nál lehet helyben definiálni, de a ciklus mögött nem él).
1p  Ciklus szervezése: osztó inicializálása, van feltétel, léptetés.
1p  Eldöntést helyesen alkalmazza, az első megtalált osztónál kilép a ciklusból.
    (Ha végigszámolja az osztókat, és db==2, ez a pont nem jár.)
1p  Ciklusmagban oszthatóság helyes vizsgálata (ha a logikai változó nem szerepel a 
    ciklusfeltételben, de az if-ben break; van, az előző pont arra is jár.
1p  Megvizsgálja a találat tényét, és kiírja a kért információt.
1p  Vezérlési szerkezet: a ciklusban kell legyen egy if, ami oszthatóságot vizsgálja,
    a ciklus után egy másik if, ami a találat tényét.
Szépség:
1p  A kód esztétikája:
    a) indentálva a ciklustörzs, az if-ek belseje,
    b) a változók neve utal a szerepre (oszto, o, t; vanoszto, talalat, nemprim...)
    c) rendeltetésszerű ciklushasználat, pl. nincs külön sorban írt i=0
       miatt "lyukas", hiányos for (; i<hossz; i+=1)

2. Háromszög golyókból

  o
 ooo
ooooo

Láttuk az eddigiek során, hogy a képernyőre balról jobbra, fentről lefelé haladva tudunk írni, ahogy a kézírással is. Ebből kiindulva írjunk programot, amely kér a felhasználótól egy számot (n), és utána egy akkora háromszöget rajzol a képernyőre „o” betűkből, hogy annak éppen a megadott számú sora van! Például n=3 esetén az oldalt látható ábra keletkezzen.

Megoldás
···o
··ooo
·ooooo
ooooooo

A megoldás elve

A feladat megoldásának kulcsa egy rajz készítése… És annak meghatározása, hogy melyik sorban hány golyó és hány szóköz van. Aki ide eljut, annak nyert ügye van.

  • Minden sor egyforma: sok szóköz, aztán sok golyó. A rajzon pötty helyettesíti a kiírandó szóközöket.
  • Egy sor kiírása: ciklusban szóköz, ciklusban golyó. Aztán új sor kezdése.
  • Ha ez megvan, ezt kell ciklusba tenni, ami a sorokon fut végig.
#include <stdio.h>

int main(void) {
    int magas;
    printf("Milyen magas legyen a kupac? ");
    scanf("%d", &magas);

    /* sorok ciklusa */
    for (int sor = 0; sor < magas; sor = sor+1) {
        /* a sor elejen kell valamennyi szokoz */
        for (int x = 0; x < magas-sor-1; x = x+1)
            printf(" ");
        /* utana valahany darab o */
        for (int x = 0; x < sor*2+1; x = x+1)
            printf("o");
        /* sor vege */
        printf("\n");
    }

    return 0;
}

Hasonló feladatok

Ha ennek a feladatnak a megoldását a gyakorlaton nehezen értetted meg, vagy nem tudnád önállóan megoldani, a példatárban itt találsz hasonlókat, amiken gyakorolhatsz otthon.

3. Prímtényezős felbontás

 75│3
 25│5
  5│5
  1│

Ez egy általános iskolában tanult algritmus – bár ott csak végrehajtani kellett tudni, nem pedig algoritmusként, sőt programkódként megfogalmazni. Írjunk most programot C-ben, amelyik kér egy számot a felhasználótól, és kiírja a prímtényezős felbontását!

Megoldás

A megoldás elve

  • Addig kell végezni a kísérletezést – osztogatást, amíg 1-hez nem jutunk. Egy while (szam != 1)-ünk máris van!
  • Ha osztható a szám, elosztjuk, kiírjuk. Ha nem osztható, másik osztót kell keresni.
  • Növekvő sorban kell haladni az osztók vizsgálatával.
  • Baj, ha megpróbálunk összetett számmal osztani (pl. 4-gyel, 6-tal)? Miért?

C-ben a % operátorral jelöljük az osztás maradékát (osztandó%osztó → maradék, pl. 9%7 értéke 2). Ezzel tudunk oszthatóságot vizsgálni.

A lenti megoldás mindig egy egész sort ír ki, pl. 150|2. Egy kiírás akkor történik meg, amikor az oszthatóságot már megvizsgáltuk, és a maradék nullának adódott. A 150|2 azt jelenti, hogy a 150-et osztjuk el 2-vel; ezért a konkrét osztás előtt írjuk ki a sort, hogy még az osztás előtti szám látszódjon. Így aztán az utolsó sort, amelyben az 1-es van, külön utasítással kell kiírni.

#include <stdio.h>

int main(void) {
    int szam;
    printf("Melyik számot? ");
    scanf("%d", &szam);

    int oszto;
    oszto = 2;
    while (szam > 1) {
        if (szam % oszto == 0) { /* ha osztható */
            printf("%5d|%d\n", szam, oszto);
            szam = szam / oszto;
        }
        else
            oszto = oszto + 1;  /* ha nem */
    }
    printf("%5d\n", 1);

    return 0;
}

Esetleg van, akinek jobban tetszik a következő megoldás: mindig kiírjuk előre a számot, amit osztani fogunk, és az oszthatóság vizsgálata után már csak az osztót. Ez is helyes megoldás. Ilyenkor az eredeti számot ki kell írni a ciklus előtt külön. A végén viszont az 1-et nem, mert az a legutolsó osztásnál megjelenik:

#include <stdio.h>

int main(void) {
    int szam;
    printf("Melyik számot? ");
    scanf("%d", &szam);

    int oszto;
    oszto = 2;
    printf("%5d|", szam);
    while (szam > 1) {
        if (szam % oszto == 0) { /* ha osztható */
            printf("%d\n", oszto);
            szam = szam / oszto;
            printf("%5d|", szam);
        }
        else
            oszto = oszto + 1; /* ha nem */
    }

    return 0;
}

Érdemes elgondolkozni azon, hogy jobb lenne-e előbb meghatározni a prímszámokat, hogy csak azokkal kelljen osztani. Mennyivel lenne hatékonyabb az a program?

Hasonló feladatok

Ha ennek a feladatnak a megoldását a gyakorlaton nehezen értetted meg, vagy nem tudnád önállóan megoldani, a példatárban itt találsz hasonlókat, amiken gyakorolhatsz otthon.

4. Római számok

Írjunk egy programot, amely kér a felhasználótól egy tízes számrendszerben megadott számot, utána pedig kiírja annak megfelelőjét római számokkal. Pl. 89→LXXXIX. Elég csak 99-ig; ha 100-at, vagy annál nagyobbat írt be a felhasználó, akkor szóljunk neki, hogy csak 99-ig működik a program.

Megoldás

A megoldás elve

  • Csökkenő sorrendben kell haladni.
  • Amit kiírtunk, azt levonhatjuk egy változóból. pl. 89 esetén a 80 (LXXX) kiírása után olyan, mintha eredetileg is csak 9 lett volna a kiírandó szám.

Rajzoljunk folyamatábrát vagy struktogramot a programhoz! Először írjunk pár példát római számra, és figyeljük meg, milyen sorrendben kell kiírni az egyes értékeket jelző betűket és betűpárokat! Vegyük észre, hogy az XC (90) csak egyszer szerepelhet, míg pl. az X (10) többször is!

#include <stdio.h>

int main(void) {
    int x;

    printf("Mi a szám? ");
    scanf("%d", &x);
    if (x < 1)
        printf("Pozitiv szamot kerek!\n");
    else if (x > 99)
        printf("Csak 99-ig tudom.\n");
    else {
        if (x >= 90) { printf("XC"); x = x-90; }
        if (x >= 50) { printf("L"); x = x-50; }
        if (x >= 40) { printf("XL"); x = x-40; }
        while (x >= 10) { printf("X"); x = x-10; }
        if (x >= 9) { printf("IX"); x = x-9; }
        if (x >= 5) { printf("V"); x = x-5; }
        if (x >= 4) { printf("IV"); x = x-4; }
        while (x >= 1) { printf("I"); x = x-1; }
        printf("\n");
    }

    return 0;
}
Struktogram részlet a római számok programhoz

Lehetne máshogy is? Mi történne, ha mindenhol ciklust használnánk? Láthatóan mindegyik számjegynél ugyanazt kell csinálni (kiírni és csökkenteni), ezért aztán a program egy nagy sorminta lett. Egy későbbi gyakorlaton majd javítunk ezen, most egyelőre még hiányzik hozzá a megfelelő C nyelvi eszköz.

5. További feladatok

Tükörszámok

A feladat a következő: írjuk ki azokat a háromjegyű számokat, amelyek megegyeznek a tükörképükkel. Például a 121 ilyen, a 123 nem. Rajzoljunk struktogramot vagy folyamatábrát a programhoz!

Megoldás

Az okos megoldás itt az, ha rájövünk, hogy végülis ezek a számok úgy alakulnak ki, hogy 10-től 99-ig számolunk, és minden szám mellé odaírjuk még egyszer az első számjegyet (pl. 10 mellé az 1-et, 101, vagy 57 mellé az 5-öt, 575). Érdemes ehhez két egymásba ágyazott ciklust írni. Az első számjegy nem lehet 0-s, a második, középső viszont igen, erre figyelni kell.

Megfigyelhetjük azt, hogy mindkét ciklus számlálásos jellegű: a külső 1-től 9-ig megy, a belső 0-tól 9-ig. Az alsó ábra egy nem szabályos, de nagyon szemléletes rajzot tartalmaz. Hasonlítsuk össze a két megoldást áttekinthetőség szempontjából!

Struktogram a tükörszámok programhoz
#include <stdio.h>

int main(void) {
    int a, b;

    a = 1;
    while (a < 10) {
        b = 0;
        while (b < 10) {
            /* aba: tükörszám! */
            printf("%d%d%d ", a, b, a);
            b = b + 1;
        }
        a = a + 1;
    }

    return 0;
}
Struktogram a tükörszámok programhoz, for ciklussal
#include <stdio.h>

int main(void) {
    for (int a = 1; a <= 9; a = a + 1)
        for (int b = 0; b <= 9; b = b + 1)
            printf("%d%d%d ", a, b, a);

    return 0;
}

Gyök kettő

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. Az előbbi túl kicsi, az utóbbi már túl nagy, hogy a gyök lehessen, mivel 12=1 és 22=4. Menjünk tovább ugyanezzel a gondolattal, és határozzuk meg a tizedesvessző utáni első számjegyet:

SzámNégyzet
1,01,00 – kevés
1,11,21 – kevés
1,21,44 – kevés
1,31,69 – kevés
1,41,96 – kevés
1,52,25 – sok

Ebből tudjuk, hogy a gyök 1,4-gyel kezdődik. A következő számjegy:

SzámNégyzet
1,401,9600 – kevés
1,411,9881 – kevés
1,422,0164 – sok

Vagyis 1,41 a keresett szám eleje. Így folytathatjuk. Írjunk programot, amely ezzel a módszerrel meghatározza a √2 első tíz számjegyét!

Megoldás
#include <stdio.h>

int main(void)
{
    double tipp = 1;
    double delta = 0.1;
    for (int i = 1; i <= 10; i = i+1) {
        while (tipp*tipp < 2) {
            tipp = tipp + delta;
        }
        tipp = tipp - delta;   /* miért? */
        delta = delta / 10;
    }
    printf("%.10f",tipp);

    return 0;
}