• Nem Talált Eredményt

A RAM-gép

In document Algoritmusok bonyolultsága (Pldal 26-31)

A RAM-gép (RAM=Random Access Memory =Közvetlen Elérésű Memó-ria) a Turing-gépnél bonyolultabb, de a valódi számítógépekhez közelebb álló matematikai modell. Mint a neve is mutatja, a legfőbb pont, ahol a Turing-gépnél többet „tud” : memóriarekeszeit közvetlenül lehet elérni (beleírni vagy belőle kiolvasni). Sajnos a RAM-gépnek ezért az előnyös tulajdonságáért fi-zetni kell: ahhoz, hogy közvetlenül el tudjunk érni egy memóriarekeszt, azt meg kell címezni; a címet valamelyik másik rekeszben tároljuk. Mivel nem korlátos a memóriarekeszek száma, a cím sem korlátos, és így a címet tar-talmazó rekeszben akármilyen nagy természetes számot meg kell engednünk.

Ennek a rekesznek a tartalma maga is változhat a program futása során (in-direkt címzés). Ez a RAM-gép erejét tovább növeli; viszont azzal, hogy az egy rekeszben tárolható számot nem korlátozzuk, ismét eltávolodunk a létező számítógépektől. Ha nem vigyázunk, a RAM-gépen a nagy számokkal végzett műveletekkel „visszaélve” olyan algoritmusokat programozhatunk be, melyek létező számítógépeken csak sokkal nehezebben, lassabban valósíthatók meg.

A RAM-gépben van egy programtár és egy memória. A memória végte-len sok memóriarekeszből áll, melyek az egész számokkal vannak címezve.

Azimemóriarekesz egyx[i]egész számot tartalmaz, ezek közül mindig csak véges sok nem 0. A programtár ugyancsak végtelen sok, 0,1,2, . . .-vel cím-zett rekeszből,sorbóláll; ebbe olyan (véges hosszúságú) programot írhatunk, mely valamely gépi kód-szerű programnyelven van írva. Például elegendő a következő utasításokat megengedni:

x[i] := 0; x[i] :=x[i] + 1; x[i] :=x[i]−1;

x[i] :=x[i] +x[j]; x[i] :=x[i]−x[j]; x[x[i]] :=x[j]; x[i] :=x[x[j]];

IF x[i]≤0 THEN GOTO p.

Ittiésjvalamely memóriarekesz sorszáma (tehát tetszőleges egész szám), ppedig valamelyik programsor sorszáma (tehát tetszőleges természetes szám).

Az utolsó előtti két utasítás biztosítja közvetett címzés lehetőségét. Elmé-leti szempontból nincs azonban különösebb jelentősége, hogy mik ezek az utasítások; csak annyi lényeges, hogy mindegyikük könnyen megvalósítha-tó műveletet fejezzen ki, és eléggé kifejezőek legyenek ahhoz, hogy a kívánt számításokat el tudjuk végezni; másrészt véges legyen a számuk. Például i ésj értékére elég volna csak a−1,−2,−3 értékeket megengedni. Másrészről bevehetnénk a szorzást stb.

A RAM-gép bemenete egy természetes számokból álló véges sorozat, mely-nek a hosszát azx[0]memóriarekeszbe, elemeit pedig rendre azx[1], x[2], . . . memóriarekeszekbe írjuk be. Ha az inputban nem engedjük meg a 0 értéket, akkor a hosszra azx[0]rekeszben nincs szükségünk, mert könnyen kiszámol-hatjuk magunk is. A RAM-gép a fenti utasításokból álló tetszőleges véges programot értelemszerűen végrehajt; akkor áll meg, ha olyan programsorhoz ér, melyben nincsen utasítás. Akimenetenazx[i]rekeszek tartalmát értjük.

A RAM-gép lépésszáma nem a legjobb mértéke annak, hogy „mennyi ideig dolgozik”. Amiatt ugyanis, hogy egy lépésben akármilyen nagy természetes számokon végezhetünk műveletet, olyan trükköket lehet csinálni, amik a gya-korlati számításoktól igen messze vannak. Pl. két igen hosszú természetes szám összeadásával vektorműveleteket szimulálhatnánk. Ezért szokásosabb a RAM-gépek lépésszáma helyett a futási idejükről beszélni. Ezt úgy defi-niáljuk, hogy egy lépés idejét nem egységnyinek vesszük, hanem annyinak, mint a benne fellépő természetes számok (rekeszcímek és tartalmak) kettes számrendszerbeli jegyeinek száma. (Mivel ez lényegében a kettes alapú loga-ritmusuk, szokás ezt a modelltlogaritmikus költségű RAM-gépnekis nevezni.) Meg kell jegyeznünk, hogy ha a RAM-gép utasításkészletét kibővítjük, pl. a szorzással, akkor ezen fajta lépések idejét úgy kell definiálni, hogy hosszú szá-mokra a lépés pl. Turing-gépen ennyi idő alatt tényleg szimulálható legyen.

Szokás két paraméterrel is jellemezni a futási időt, hogy „a gép legfeljebb nlépést végez legfeljebb k jegyű (a kettes számrendszerben) számokon” ; ez tehátO(nk)futási időt ad.

1.3.1. Feladat. Írjunk olyan programot a RAM-gépre, mely

a) előre adott k pozitív egész számra az üres bemeneten előállítja a k kimenetet;

b) a kbemenet esetén meghatározza azt a legnagyobbmszámot, melyre 2m≤k;

c) a k bemenet esetén kiszámítjak kettes számrendszerbeli alakját (a k számi. bitjét írja azx[i]rekeszbe);

d) ha a bemenet k ésℓ pozitív egész számok (az x[1] ésx[2]rekeszben), kiszámítja a szorzatukat.

Hakésℓszámjegyeinek száman, akkor a programO(n)lépést tegyenO(n) jegyű számokkal.

Most megmutatjuk, hogy a RAM-gép és a Turing-gép lényegében ugyan-azt tudják kiszámítani, és a futási idejük sem különbözik túlságosan. Te-kintsünk egy (egyszerűség kedvéért) egyszalagos T Turing-gépet, melynek ábécéje{0,1,2}, ahol (a korábbiaktól eltérően, de itt célszerűbben) a 0 legyen az üres-mező jel.

A Turing-gép mindenx1. . . xnbemenete (mely egy 1-2 sorozat) kétfélekép-pen is tekinthető a RAM-gép egy bemenetének: beírhatjuk azx1, . . . , xn szá-mokat rendre azx[1], . . . , x[n]rekeszekbe, vagy megfeleltethetünk azx1. . . xn

sorozatnak egyetlen természetes számot pl. úgy, hogy a ketteseket 0-ra cse-réljük és az elejére egy egyest írunk; és ezt a számot írjuk be azx[0]rekeszbe.

A Turing-gép kimenetét is hasonlóképpen értelmezhetjük, mint a RAM-gép kimenetét.

Csak az első értelmezéssel foglalkozunk, a második szerinti bemenet az 1.3.1. b) feladat alapján átalakítható az első értelmezés szerintivé.

1.3.1. Tétel. Minden{0,1,2}feletti Turing-géphez konstruálható olyan prog-ram aRAM-gépen, mely minden bemenetre ugyanazt a kimenetet számítja ki, mint a Turing-gép, és ha a Turing-gép lépésszámaN, akkor aRAM-gépO(N) lépést végezO(logN)jegyű számokkal.

Bizonyítás. LegyenT={1,{0,1,2},Γ, α, β, γ}. LegyenΓ={1, . . . , r}, ahol1 =

=START ésr=STOP. A Turing-gép számolásának utánzása során a RAM-gép2i-edik rekeszében ugyanaz a szám (0, 1, vagy 2) fog állni, mint a Turing-gép szalagjánaki-edik mezején. Az x[1] rekeszben tároljuk, hogy hol van a fej a szalagon (azaz a szalag-pozíció kétszeresét, mert az ennyiedik memória-rekeszben tároljuk ezt a tartalmat), a vezérlőegység állapotát pedig az fogja meghatározni, hogy hol vagyunk a programban.

ProgramunkPi (1≤i≤r) ésQi,j (1≤i≤r−1, 0≤j≤2) részekből fog összetevődni. Az alábbiPiprogramrész (1≤i≤r−1) azt utánozza, amikor a

Turing-gép vezérlőegységeiállapotban van, és a gép kiolvassa, hogy a szalag x[i]/2-edik mezején milyen szám áll. Ettől függően fog más-más részre ugrani a programban:

x[3] :=x[x[1]];

IFx[3]≤0THEN GOTO[Qi,0 címe];

x[3] :=x[3]−1;

IFx[3]≤0THEN GOTO[Qi,1 címe];

x[3] :=x[3]−1;

IFx[3]≤0THEN GOTO[Qi,2 címe];

APrprogramrész álljon egyetlen üres programsorból. Az alábbiQi,j prog-ramrész átírja azx[1]-edik rekeszt a Turing-gép szabályának megfelelően, mó-dosítjax[1]-et a fej mozgásának megfelelően, és az újaállapotnak megfelelő Pa programrészre ugrik:

x[3] := 0;

x[3] :=x[3] + 1;

...

x[3] :=x[3] + 1;



β(i, j)-szer x[x[1]] :=x[3];

x[1] :=x[1] +γ(i, j);

x[1] :=x[1] +γ(i, j);

x[3] := 0;

IFx[3]≤0THEN GOTO[Pα(i,j)címe];

(Itt az x[1] :=x[1] +γ(i, j)utasítás úgy értendő, hogy az x[1] :=x[1] + 1, ill.x[1] :=x[1]−1 utasítást vesszük, haγ(i, j) = 1vagy−1, és elhagyjuk, ha γ(i, j) = 0.) Maga a program így néz ki:

x[1] := 0;

P1

P2

... Pr

Q0,0

... Qr−1,2

Ezzel a Turing-gép „utánzását” leírtuk. A futási idő megbecsléséhez elég azt megjegyezni, hogyN lépésben a Turing-gép legfeljebb egy−Nés+Nközötti sorszámú mezőbe ír bármit is, így a RAM-gép egyes lépéseiben legfeljebb O(logN)hosszúságú számokkal dolgozunk.

Megjegyzés. Az 1.3.1. tétel bizonyításában nem használtuk fel azy:=y+z utasítást; erre az utasításra csak az 1.3.1. feladat megoldásában van szükség.

Sőt ez a feladat is megoldható volna, ha ejtenénk a lépésszámra vonatkozó kikötést. Azonban ha a RAM-gépen tetszőleges egész számokat megengedünk bemenetként, akkor enélkül az utasítás nélkül exponenciális futási időt, sőt lépésszámot kapnánk igen egyszerű problémákra is. Például tekintsük azt a feladatot, hogy azx[1]regiszteratartalmát hozzá kell adni azx[0]regiszterb tartalmához. Ez a RAM-gépen könnyen elvégezhető néhány lépésben; ennek futási ideje még logaritmikus költségek esetén is csak kb.log|a|+ log|b|. De ha kizárjuk azy:=y+z utasítást, akkor legalábbmin{|a|, |b|}idő kell hoz-zá (ugyanis minden más utasítás a maximális tárolt shoz-zám abszolút értékét legfeljebb 1-gyel növeli).

Legyen most adott egy program a RAM-gépre. Ennek be- és kimenetét egy-egy {0,1,−,#}-beli szónak tekinthetjük (a szereplő egész számokat kettes számrendszerben, ha kell előjellel felírva; két memória-rekesz tartalma közé pedig # jelet rakva). Ebben az értelemben igaz az alábbi tétel:

1.3.2. Tétel. MindenRAM-gépre írt programhoz van olyan Turing-gép, mely minden bemenetre ugyanazt a kimenetet számítja ki, mint aRAM-gép, és ha aRAM-gép futási ideje N, akkor a Turing-gép lépésszáma O(N2).

Bizonyítás. A RAM-gép számolását négyszalagos Turing-géppel fogjuk szi-mulálni. Turing-gépünk első szalagjára írhatnánk sorban az x[i] memória-rekeszek tartalmát (kettes számrendszerben, ha negatív, előjellel ellátva, # jelekkel elválasztva), minden rekesz tartalmát sorra feltüntetve (a 0 tarta-lom mondjuk a „∗” jelnek felelne meg). Problémát jelent azonban, hogy a RAM-gép akár a2N−1sorszámú rekeszbe is írhat csakN időt véve igénybe, a logaritmikus költség szerint. Természetesen ekkor a kisebb indexű rekeszek túlnyomó többségének a tartalma 0 marad az egész számítás folyamán; ezek-nek a tartalmát nem célszerű a Turing-gép szalagján tárolni, mert akkor a szalag nagyon hosszú részét használjuk, és exponenciális időt vesz igénybe csak amíg a fej ellépeget oda, ahová írnia kell. Ezért csak azoknak a rekeszek-nek a tartalmát tároljuk a Turing-gép szalagján, melyekbe ténylegesen ír a RAM-gép. Persze ekkor azt is fel kell tüntetni, hogy mi a szóban forgó rekesz sorszáma.

Azt tesszük tehát, hogy valahányszor a RAM-gép egy x[z] rekeszbe egy y számot ír, a Turing-gép ezt úgy szimulálja, hogy az első szalagja végére a

##y#zjelsorozatot írja. (Átírni soha nem ír át ezen a szalagon!) Ha a RAM-gép egyx[z]rekesz tartalmát olvassa ki, akkor a Turing-gép első szalagján a fej hátulról indulva megkeresi az első##u#zalakú sorozatot; ez azuérték adja meg, hogy mi volt utoljára a z-edik rekeszbe írva. Ha ilyen sorozatot nem talál, akkorx[z]-t 0-nak tekinti. Könnyű az elején RAM-gép bemenetét is ilyen formátumúra átírni.

A RAM-gép „programnyelvének” minden egyes utasítását könnyű szimu-lálni egy-egy alkalmas Turing-géppel, mely csak a másik három szalagot hasz-nálja.

Turing-gépünk olyan „szupergép” lesz, melyben minden programsornak megfelel egy rész-Turing-gép, és az ehhez tartozó állapotok egy halmaza. Ez a rész-Turing-gép az illető utasítást végrehajtja, és a végén a fejeket az első szalag végére (utolsó nemüres mezejére), a többi szalagnak pedig a 0-adik me-zejére viszi vissza. Minden ilyen Turing-gép STOP állapota azonosítva van a következő sornak megfelelő Turing-gép START állapotával. (A feltételes ugrás esetén, hax[i]≤0 teljesül, apsornak megfelelő Turing-gép kezdőálla-potába megy át a „szupergép”.) A 0-adik programsornak megfelelő Turing-gép START-ja lesz a szupergép START-ja is. Ezenkívül lesz még egy STOP ál-lapot; ez felel meg minden üres programsornak.

Könnyű belátni, hogy az így megkonstruált Turing-gép lépésről lépésre szimulálja a RAM-gép működését. A legtöbb programsort a Turing-gép a benne szereplő számok számjegyeinek számával, vagyis éppen a RAM-gépen erre fordított idejével arányos lépésszámban hajtja végre. Kivétel egy x[i]

érték kiolvasása, melyhez esetleg (egy lépésnél legfeljebb kétszer) végig kell keresni az egész szalagot. Mivel a szalag hossza legfeljebb 52N (a nem # jelek száma legfeljebbN), a teljes lépésszámO(N2).

1.3.2. Feladat. Legyen p(x) =a0+a1x+· · ·+anxn egy egész együtthatós polinom. Írjunk olyan programot a RAM-gépre, mely aza0, . . . , anbemenetre kiszámítja ap2(x)polinom együtthatóit. Becsüljük meg a futási időtnésK=

= max{|a0|, . . . ,|an|}függvényében.

1.3.3. Feladat. A RAM-gépeknél az univerzalitás fogalmára látszólag nincs szükségünk, mivel maga a gép univerzális bizonyos értelemben. Azonban lás-suk be az alábbi, „ön-szimulációs” tulajdonságot:

Egypprogramra ésxbemenetre jelöljeR(p, x)a RAM-gép kimenetét. Je-löljehp, xiazt a bemenetet, amit úgy kapunk, hogy approgramot betűnként (pl. ASCII kódolással) bemásoljuk az első néhány memóriarekeszbe, majd egy elválasztójel (pl. a # karakter kódja) után bemásoljuk azxeredeti bemene-tet. Bizonyítsuk be, hogy van olyanuprogram, hogy mindenpprogramra és xbemenetreR(u,hp, xi) =R(p, x).

In document Algoritmusok bonyolultsága (Pldal 26-31)