Adatszerkezetek II.

Ha a legutóbbi programváltozatban i értékét növelni kezdjük, nyomasztó lassulásnak leszünk tanúi: az első 100 szó leszármazottaihoz egy századmásodperc sem kell, az első 1000 szóéihoz 2,5 másodperc, az első 10000 szóéihoz 662. (Értelemszerűen a számok önmagukban jelentőség nélküliek, nyilván csak az arányuk érdekes.)

Felvethető erre, hogy hiszen úgyis végtelen sok időnk van, de azért érdemes lehet eltűnődni azon: hogyan tehetnénk hatékonyabbá a programot? Azt, hogy egy szót előállítottunk-e már, egy lista elemei alapján vizsgálja a program: ha minden igaz, ehhez rossz esetben a lista összes elemét meg kell néznie. Vajon a python további adatszerkezeteivel nem gyorsíthatnánk-e a program futásán?

A lista mellett elhelyezhetnénk egy halmazban is a szavakat – ezzel nyilván nő a tárigény, de vajon mennyit nyerünk a gyorsabb kereséssel? Vagy ha a szavak egy szótár kulcsai lennének, akkor akár a hozzájuk vezető utat is tárolhatnánk…

Lássuk most az ezt megvalósító programot.

def rule1(list, dict, i):
    word= list[i]
    if word[-1]=='I':
        neww= word+'U'
        if neww not in dict:
            list+= [neww]
            dict[neww]= dict[word]+'U'

def rule2(list, dict, i):
    word= list[i]
    neww= word+word[1:]
    if neww not in dict:
        list+= [neww]
        dict[neww]= dict[word]+'D'

def rule3(list, dict, i):
    word= list[i]
    for j in range(1, len(word)-2):
        if word[j:j+3]=='III':
            neww= word[:j]+'U'+word[j+3:]
            if neww not in dict:
                list+= [neww]
                dict[neww]= dict[word]+'H'+str(j)

def rule4(list, dict, i):
    word= list[i]
    for j in range(1, len(word)-1):
        if word[j:j+2]=='UU':
            neww= word[:j]+word[j+2:]
            if neww not in dict:
                list+= [neww]
                dict[neww]= dict[word]+'E'+str(j)

def rules(list, dict, i):
    rule1(list, dict, i)
    rule2(list, dict, i)
    rule3(list, dict, i)
    rule4(list, dict, i)
    return i+1

i= 0
list= [ 'MI' ]
dict= { 'MI': ''}
while i<11:
    i= rules(list, dict, i)
print(dict)

Ahogy korábban beharangoztuk, visszakerült a négy függvénybe annak vizsgálata, hogy a szót korábban megtaláltuk-e (illetve kihasználtuk, hogy a lista mellett a szótár is mutabilis adatszerkezet: a függvények mindkettőnek megváltoztathatják az értékét, így már csak az immutabilis i változó értékét kell visszaadniuk).

És végül a szótár tartalma (ezúttal nem csak áttördelve, hanem át is rendezve: az átrendezés a fabejárás sorrendjének felel meg, ami egyúttal –a levezetési szabályokat tekintve– sajátos ábécésorrendet is tükröz):

{
 'MI': '',
 'MIU': 'U',
 'MII': 'D',
 'MIUIU': 'UD',
 'MIIU': 'DU',
 'MIIII': 'DD',
 'MIUIUIUIU': 'UDD',
 'MIIUIIU': 'DUD',
 'MIIIIU': 'DDU',
 'MIIIIIIII': 'DDD',
 'MUI': 'DDH1',
 'MIUIUIUIUIUIUIUIU': 'UDDD',
 'MIIUIIUIIUIIU': 'DUDD',
 'MIIIIUIIIIU': 'DDUD',
 'MUIU': 'DDUH1',
 'MIUU': 'DDUH2',
 'MIIIIIIIIU': 'DDDU',
 'MIIIIIIIIIIIIIIII': 'DDDD',
 'MUIIIII': 'DDDH1',
 'MIUIIII': 'DDDH2',
 'MIIUIII': 'DDDH3',
 'MIIIUII': 'DDDH4',
 'MIIIIUI': 'DDDH5',
 'MIIIIIU': 'DDDH6',
 'MUIUI': 'DDH1D'
}

A sebességnövekedés lenyűgöző: azzal, hogy az első 10000 szó 145294 leszármazottjának levezetése során szótárban kerestük a már előállított szavakat, a program majdnem három nagyságrenddel rövidebb idő alatt fejeződik be.