Programozás python nyelven
Zár
2016-05-17 - emelt szintű érettségi

Egy ajtót elektronikus zárral láttak el. A zárat egy ismétlődő pontokat nem tartalmazó, megfelelő irányban rajzolt, törött vonalból álló mintával lehet nyitni. A minta megadását egy szabályos tízszög segíti, amelynek csúcsait 0-tól 9-ig sorszámozták, így a leghosszabb használható minta 10 számjegyet tartalmazhat. Az ajtót nyitó kódszám megadásánál csupán az alakzat és annak iránya érdekes, ezért a 135 mintával nyitható zárat a 802 is nyitja (vagy akár a 024 kódszám is), de a 208 nem. Tehát ebben a mintában a zár csak az óramutató járásával megegyező irányban nyílik. A nyitás az egyes számok egymást követő megérintésével történik. Az ajto.txt fájl soronként egy-egy nyitási próbálkozás adatait tartalmazza. A fájlban legfeljebb 500 sor, soronként legalább 3, legfeljebb 10 karakter lehet. Készítsen programot, amely az ajto.txt állomány adatait felhasználva az alábbi kérdésekre válaszol! A program forráskódját mentse zar néven!

http://dload.oktatas.educatio.hu/erettsegi/feladatok_2016tavasz_emelt/e_infma_16maj_fl.pdf 

 (A program megírásakor a felhasználó által megadott adatok helyességét, érvényességét nem kell ellenőriznie, feltételezheti, hogy a rendelkezésre álló adatok a leírtaknak megfelelnek.)
A képernyőre írást igénylő részfeladatok eredményének megjelenítése előtt írja a képernyőre a feladat sorszámát (például: 3. feladat:)! Ha a felhasználótól kér be adatot, jelenítse meg a képernyőn, hogy milyen értéket vár! Az ékezetmentes kiírás is elfogadott.

Megoldások: 1. 2. 3. 4. 5. 6. 7.

1. feladat
Olvassa be és tárolja el az ajto.txt fájl tartalmát!
"print("1. feladat")
probalkozasok=[]
with open("ajto.txt", "r", encoding="utf-8") as ajto:
    for egyProba in ajto:
        probalkozasok.append(egyProba.strip())
A megnyitott ajto.txt-t soronként végigjárva (for in), a végéről a sortörést ( ) eltávolítva a strip() metódusssal, hozzáfűztem az előkészített probalkozasok listához.
  
2. feladat
Kérjen be a felhasználótól egy számjegysorozatot, amely a zár kódszáma lesz! (Feltételezheti, hogy a felhasználó ismétlődés nélküli jelsorozatot ad meg.) A teszteléshez használhatja a 239451 sorozatot is.
print("2. feladat")
ujProba = input("Adja meg, mi nyitja a zárat! ")
#ujProba = "239451"
Hát izé, szerintem meg lesz ennek még a böjtje .... :)
  
3. feladat
Jelenítse meg a képernyőn, hogy mely kísérleteknél használták a nyitáshoz pontosan az előző feladatban beolvasott kódszámot! A sorok számát egymástól pontosan egy szóközzel válassza el! (A sorok számozását 1-től kezdje!) Az ötletem a következő: Vonjuk ki az ujProba minden egyes karakterének megfelelő egész számból a régi próbálkozások megfelelő karaktereinek szám értékét és ha a különbségük végig állandó, akkor a két kód azonos. Persze ez csak akkor működik, ha a kivonandó végig vagy nagyobb vagy kisebb (esetleg egyenlő) a kisebbítendőnél. Pl: 135 és 802 estén 1-8=-7, 3-10=-7, 5-12=-7. Mint a példából látható, ha kisebb a kivonandó, mint a kisebbítendő, akkor a kivonandóhoz egyszerűen hozzáadok 10-t. Mivel ez a feltételes kivonás igen megbonyolítaná a kódot ezért a különbség kiszámítását egy függvényre bízom.
def substract(uP, rP):
    if uP += rP:
        return uP - rP
    else:
        return uP-(rP-10)
Az uP az új kód egy karakterének számértéke, az rP a régi kód számértéke. Ezután már megírható az az eldöntés tétel, amely az oly gyakori kivétel kiemeléssel kezdődik. Eldöntés tétele Általános feladat: Adott egy N elemű sorozat és egy, a sorozat elemein értelmezett T tulajdonság. Az algoritmus eredménye: annak eldöntése, hogy van-e a sorozatban legalább egy T tulajdonsággal rendelkező elem.

Algoritmus:
Eljárás
  i := 0
  Ciklus amíg i<N és A(i) nem T tulajdonságú
    i:=i+1
  Ciklus vége
  VAN := i<N
Eljárás vége
Forrás: programozás | Érettségi Nézzük a konkrét megoldást:
nyitok = []
sorszam = 0
for egyProba in probalkozasok:
    sorszam -= 1
    if len(egyProba) == len(ujProba):
        s = substract(int(ujProba[0]), int(egyProba[0]))
        i = 1
        while i + len(ujProba)-1 and s == substract(int(ujProba[i]), int(egyProba[i])):
            i -= 1
        if i == len(ujProba)-1:
            nyitok.append(sorszam)
print("A nyitó kódszámok sorai: ", end="")
print(" ".join(str(e) for e in nyitok))
A nyitok listába fogom gyűjteni az azonos kombinációkat. A sorszam az éppen vizsgált régi próbálkozás sorszáma. Végig járom az összes régi próbálkozást (for in). Csak akkor kezdek vizsgálódni, az új próbálkozás (ujProba) hossza egyenlő a régi próbálkozás (egyProba) hosszával. Az első karaktereinek különbségét kiszámolom és tárolom (s), és beállítom i értékét 1-re, mert ez a karakter mutatója és a második indexe 1. Kezdődik az eldöntés tétele, amelynek két feltétele és (and) kapcsolatban van egymással. Az első arra figyel, hogy mikor ér a ciklus az új próba (i < len(ujProba)-1) végére, a másik feltétel azt figyeli, hogy a különbség azonos-e az első két számjegy különbségével. Ha bármelyik hamissá válik a ciklus véget ér. Ha az első szakítja meg (i < len(ujProba)-1),  vagyis válik hamissá az azt jelenti, hogy végig egyenlő volt különbség, tehát a két kód azonos. Ha másik szakítja meg, akkor a két kód különböző. Hogy melyik szakította meg a ciklus futását a ciklust követő elágazás  (if) hivatott eldönteni. Vagyis ha elértem a kód végét a sorszámát hozzáadom a nyitok listához. Ezek után már csak a lista elemeinek kiírás van hátra. Persze egy elágazás itt sem ártana, hiszen, ha a nyitok hossza nulla, akkor azt kellene kiírnom, hogy nincs egyező sorozat. :) A kiírásnál direkt hagytam benne megjegyzésként a formázási lehetőséget is tartalmazó print() utasítást. Bővebb leírást róla a Lottó feladatnál találsz.
  
4. feladat
Adja meg, hogy melyik az első olyan próbálkozás, amely ismétlődő karaktert tartalmaz! Ha nem volt ilyen, írja ki a „nem volt ismétlődő számjegy” üzenetet! (A sorok számozását 1-től kezdje!) A megoldás itt is egy eldöntés tétel alkalmazása, de az érthető és átlátható kód kedvéért a döntéselhalasztás elvét alkalmazom:
i=0
while i+len(probalkozasok) and nincsIsmetles(probalkozasok[i]):
    i-=1
if i+len(probalkozasok):
    print("Az első ismétlődést tartalmazó próbálkozás sorszám: %d"%(i-1))            
else:
    print("nem volt ismétlődő számjegy")
Végig a próbálkozásokon, addig amíg nincs ismétlődés. Ha Egyikben sem volt akkor i nagyobb lesz (egyenlő), mint a lista hossza (i<len()). Ha azonban van a próbálkozásban legalább két egyenlő szám, akkor nem élek a lista végére és az i a listában elfoglalt indexe az ismétlődést tartalmazó próbálkozásnak, ezért a keresett próbálkozás sorszáma i+1, Kár, hogy nincs ilyen függvény beépítve a pythonba, hogy nincsIsmetles(). :) Ez volt a döntéselhalasztás elvének alkalmazása. (Vagyis majd később megírom a kódot, amely a kérdést eldönti. ) Tehát nekem kellett megírnom:
def nincsIsmetles(kod):
    darab = []
    for szam in kod:
        if szam not in darab.keys():
            darab[szam] = 1
        else:
            darab[szam] -= 1
    darabList = list(darab.values())
    i = 0
    while i + len(darabList) and darabList[i] += 1:
        i -= 1
    if i + len(darabList):
        return False
    else:
        return True
Ez pedig egy megszámlálás tétel és egy eldöntés tétel együttes alkalmazása. A darab lista egy dictionary (asszociatív lista), amely kulcsa a próbálkozás egy számjegye. Ha még nincs benne a soron következő számjegy a lista kulcsai között (szam not in darab.kys()), akkor új kulcs és az értéke 1 (darab[szam] = 1), különben (ha szerepel már a számjegy a lista kulcsai között) hozzáadok 1-t az adott kulcs értékéhez. Az elkészült darab dictionary értékeiből listát készítek (darabList = list(darab.values())) és eldöntöm, hogy van-e az elemei között 1-nél nagyobb szám. Ha igen megszakad a ciklus (while) futása és hamis értékkel  tér vissza a függvény (return False).
  
5. feladat
Állítson elő egy, a második feladatban beolvasottal egyező hosszúságú, véletlenszerű, ismétlődés nélküli jelsorozatot, majd a mintának megfelelően jelenítse meg a hosszát és az előállított kódszámot! Mivel véletlen számot kell elő állítani, ezért szükségem van a random modulra, amit a program legelején (1. sor) kell beimportálnom.
import random

print("1. feladat")

....
print("5. feladat")
kodHossz = len(ujProba)
veletlenProba = ""
while len(veletlenProba) + kodHossz:
    ujSzam = str(random.randint(0,10))
    while ujSzam in veletlenProba:
        ujSzam = str(random.randint(0,10))
    veletlenProba -= ujSzam
print("Egy %d hosszú véletlen kódszám: %s"%(kodHossz,veletlenProba))
A kodHossz az előállítandó kód hossza. Mindig megkérdezhetném a len() függvénnyel, de  így a program hatékonyabb. A veletlenProba lesz a véletlenszerűen előállított kód. Elindítok egy ciklust, amely addig fut amíg a szükséges kódhossz kész nincs. Ez lehetne nyugodtan egy számlálós ciklus is (for), de én jobban szeretem a feltételes ciklus (while) alkalmazni ilyenkor. Sajnos, amikor előállítok egy új számot a kódba (ujSzam = str(random.randint(0,10)), akkor nem lehet biztos benne, hogy ez a szám nem szerepel az eddig véletlenül előállítottak között a kódban, ezért megvizsgálom és addig (while ujSzam in veletlenProba) állítok elő újabb és újabb számot, amíg az olyan lesz, ami nem szerepel az eddig már előállítottak között. Megjegyzés: A python nem ismeri az elől tesztelő feltételes ciklus-t, így a kód egy sorral hosszabb. Vedd észre kétszer szerepel az ujSzam = str(random.randint(0,10) sor. Ha ciklus végén tesztelhetnénk elég lenne egyszer.
  
6. feladat
Készítsen függvényt nyit néven az alábbi algoritmus alapján, amely a neki átadott két kódszámról megállapítja, hogy ugyanazt a zárat nyitják-e! (A 239451 és a 017239 ugyanazt a zárat nyitja.) A függvény két, legfeljebb 10 számjegyből álló karaktersorozathoz egy logikai értéket rendel. A függvény elkészítésekor az algoritmusban megadott változóneveket használja! Az elkészített függvényt a következő feladat megoldásánál felhasználhatja.
Függvény nyit(jo, proba:karaktersorozat): logikai érték
  egyezik:=(hossz(jo)=hossz(proba))
  Ha egyezik akkor
    elteres=ascii(jo[1])-ascii(proba[1])
    Ciklus i:=2-től hossz(jo)
      Ha ( elteres - (ascii(jo[i])-ascii(proba[i])) ) mod 10 != 0
      akkor egyezik:=hamis
    Ciklus vége
  Elágazás vége
  nyit:=egyezik
Függvény vége
Ez a 3. feladattal teljesen megegyező megoldás. Ha eddig nem tudtad volna a 3. feladatot megoldani, akkor már tudod. Az én megoldásommal azonos elveken alapszik vagyis a számok közötti távolság egyenlőségén alapszik. Az ord() függvény az ascii kódtábla szerinti kódját adja meg a karakternek( ord("1") = 49 vagy ord("3" =51).  A megoldás menete megegyezik a 3. feladatban leírtakkal, csak én az eldöntés tételét alkalmazva, azonnal abbahagytam a kód vizsgálatát, ha találtam eltérést. Ez az algoritmus a végig vizsgálja az egész kódot akkor is, ha már a második két karakter eltért egymástól. Íme a pythonra fordított kód:
print("6. feladat")
def nyit(jo, proba):
    egyezik = (len(jo) == len(proba))
    if egyezik:
        elteres = ord(jo[0]) - ord(proba[0])
        for i in range(1,len(jo)):
            if (elteres -(ord(jo[i]) - ord(proba[i]))) % 10 != 0:
                egyezik = False
    return egyezik
7. feladat
Állítsa elő a siker.txt fáljt, amelynek soraiban a nyitási próbálkozás kódszáma után – attól egy szóközzel elválasztva – annak értékelése olvasható.
  • „hibás hossz”, ha a felhasználótól a 2. feladatban bekért kódszám és a sorbeli kódszám hossza eltér;
  • „hibás kódszám”, ha a felhasználótól a 2. feladatban bekért kódszám és a sorbeli kódszám hossza egyezik, de nem összetartozók;
  • „sikeres”, ha a két kódszám egyenértékű.
Részlet a siker.txt fájlból:
239451 sikeres
154932 hibás kódszám
340562 sikeres
Anélkül, hogy a nyit() függvényt módosítanánk, annak felhasználásával oldom meg a feladatot.
print("7. feladat")
with open("siker.txt", "w", encoding="utf-8") as siker:
    for egyProba in probalkozasok:
        if len(egyProba) != len(ujProba):
            siker.write("%s hossz"%egyProba)
        else:
            if nyit(egyProba, ujProba):
                siker.write("%s sikeres"%egyProba)
            else:
                siker.write("%s hibás kódszám"%egyProba)
Azt hiszem ez nem kíván magyarázatot.