Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Algorithms

2,355 views

Published on

Programozói versenyfeladatok, alapvető matemetikai algoritmusok

Published in: Education, Technology
  • Be the first to comment

  • Be the first to like this

Algorithms

  1. 1. Programozói versenyfeladatok,alapvető matematikai algoritmusok Kiss Csaba Zsolt Programtervező matematikus KLTE Debrecen 1994.
  2. 2. Tartalomjegyzék Bevezetés 2 A versenyekről. 3 1. Kombinatorikai algoritmusok 6 1.1. Ismétlés nélküli permutációk. 6 1.2. Ismétléses permutációk 10 1.3. Kombinációk 13 1.4. Egy halmaz összes részhalmazai, particionálása 16 1.5. Gray kódok 23 2. Geometriai algoritmusok 26 Bevezetés 26 2.1. Szakaszok metszete 28 2.2. Poligon és pont 31 2.3. Ponthalmaz konvex burka 34 3. Gráfalgoritmusok 39 Bevezetés 39 3.1. Mélységi keresés 40 3.2. Optimális keresés 44 Függelék 52 Ábrák, programok listája 52 Problémák listája 52 1
  3. 3. BevezetésA dolgozat címében hivatkozott versenyfeladatok az ACM szervezésében rendezett egyetemiprogramozói csapatversenyek feladatai. A versenysorozatot évente rendezik és három fordulóból áll.Az első forduló általában valamilyen helyi (egyetemi, városi) verseny. Ezeket a versenyeket a helyi(lelkes) szervezők szervezik, általában önkéntes alapon. Szervezésen itt a verseny ténylegeslebonyolítását kell érteni és a feladatok kiválasztását. A helyi versenyekről tovább jutó csapatokvesznek részt a második fordulóban (egyetemenként legfeljebb egy csapat), amelyet már az ACMadott szervezete szervez. A második fordulóban (Regional Final - területi döntő) általában valamilyennagyobb területi egység (Pl. Kalifornia, Nyugat-Európa) egyetemeinek csapatai versenyeznek. Aterületi döntőkből egy-három csapat juthat tovább a döntőbe, amely mindig valamelyik USA-belinagyvárosban zajlik.A dolgozatom célja a verseny és a verseny feladatainak általános bemutatása. Egyetemi éveim alattösszesen 7 -szer vettem részt 1. és 2. fordulós versenyeken. Az itt összegyüjtött tapasztalatokat ésszép megoldásokat, a megoldásokhoz felhasmált alapvető matematikai algoritmusokat úgy éreztemmindenképpen szükséges rendszerezve megörökíteni, ehhez kínált kiváló fórumot a diplomadolgozatom.A dolgozat melléklete kb. 90 eredeti versenyfeladatot tartalmaz, amely biztosan nagyon hasznos leszaz egyetemünkön folyó első és másod éves programozó-matematikus, matematikus, informatika ésmatematika tanár szakos hallgatók különböző tantárgyainak gyakorlati oktatásához. . A feladatokangol nyelvűek, mert a versenyek hivatalos nyelve az angol.A dolgozatban található alapvető matematikai algoritmusok segítségével mintegy 30 (általában a nehezebbek közé sorolt probléma) oldható meg. Az algoritmusok kisebb módosításaival további feladatok megoldhatók. Az egyes algoritmusoknál hivatkozás található a feladatokra, amelyek megoldásához az adott algoritmus jó hatásfokkal felhasználható. Az egyes algoritmusok újabb problémákat vetnek fel (P-jelű problémák) ezek megoldása szintén jó gyakorlat lehet. Ezúton szeretnék a csapatom nevében köszönetet mondani szakmai támogatóínknak és tanárainknak akik nélkül még az így elért szerény eredményeinket sem értük volna el, név szerint: Kuki Attilának a helyi versenyek szervezéséért és az európai döntőre való kiutazás szervezéséért, valamint hasznos tanácsaiért, Dr Arató Mátyásnak, Dr Lajkó Károlynak, Dr. Juhász Istvánnak és Dr Végső Jánosnak általános támogatásaikért, Herendi Tamásnak igen hasznos szakmai tanácsaiért és az IFSz Kft. valamint az IQSoft Rt. munkatársainak szakmai és egyéb támogatásukért. Végül szeretnénk megköszönni anyagi támogatásukat azon cégeknek és szervezeteknek akik nélkül semmiképpen sem képviselhettük volna egyetemünket az európai döntőkön:A Külkereskedelmi és Hitel Bank Rt.,A Biogal Rt.,Az IQSoft Rt.,A Dataware Kft.,A KL TE Diákönkormányzata.Valamint szeretném megköszönni csapattársaimnak Fekete Zoltánnak, Jakó Jánosnak,Molnár Tamásnak és tanáraimnak Kuki Attilának és Herendi Tamásnak a dolgozat elkészüléséhezadott hasznos tanácsaikat és bölcs észrevételeiket. 2
  4. 4. A versenyekrőlA verseny szabályai:A versenyeken kezdetben 4, majd (1991 után) 3 fós csapatok indulhattak. A csapat tagjai közöttkezdetben lehetett egy diplomás is, később diplomások részvétele nem volt megengedett. A versenyideje általában 5 óra. A csapatok a verseny ideje alatt egy darab IBM PC típusú számítógépethasználhatnak a szükséges fordítóprogrammal felszerelve, ez a TURBO PASCAL 5.0 -ás verziójavolt. A csapatok a verseny alatt bármilyen írott forrást használhatnak, viszont semmilyen más (pl.mágneses) forrás használata nem megengedett csakúgy, mint a programozható zsebszámológépekhasználata sem. A csapatoknak a verseny ideje alatt 5-8 problémát kell megoldaniuk. A problémákközött semmilyen sorrendiség sincs. A verseny nyelve angol, ezért a feladatok szövege, a zsűrinekfeltett kérdések, a zsűri válaszai is angol nyelvűek.A problémák megoldásaA csapatoknak az elkészült megoldást -amely mindig egy pascal program forráskódja - az e célrafenntartott mágneslemezen kell a zsűrihez eljuttatniuk. A zsűri a kódot lefordít ja és saját inputadataival teszteli. A programoknak 1 perc futásidő áll rendelkezésére, ez alatt kell outputotprodukálniuk. Az output alapján a zsűri a következő válaszokat adhatja a csapatoknak:1. Syntax Error - fordítási hiba2. Run Time Error - Futás közbeni programhiba, pl O-val való osztás3. Time Limit Exceded - Időtúllépés4. Wrong Answer - hibás válasz.5. Accepted - ElfogadvaEgy probléma megoldásával többször is lehet próbálkozni, de zsűri csapatonként és problémánkéntméri a verseny kezdetétől a megoldáshoz felhasznált időt. Az 1.-4. esetben (és minden továbbisikertelen kísérlet után) a zsűri az adott csapatnak adott probléma megoldásához felhasznált idejét 20perccel növeli.A verseny ideje alatt az egyes feladatokkal kapcsolatban felmerült értelmezési stb. problémákkalkapcsolatban a csapatok írásban kérdéseket tehetnek fel a zsűrinek, aki szintén írásban kötelesválaszolni ezekre. A zsűri a csapatokat szabályszegés esetén kizárással sújthatja.A Kiértékelés szabályai:A verseny végeztével a zsűri összeszámolja az egyes csapatok által megoldott problémák számát, ésösszeadja a helyesen megoldott problémákhoz felhasznált időket. Így minden csapat eredménye kétmennyiségből áll: - A megoldott problémák száma - Az ehhez felhasznált idő Az a csapat a verseny győztese, amely a legtöbb feladatot oldotta meg, ha ilyen több van akkor a verseny győztese az a csapat, amely a legkevesebb időt használta fel. Hazai Versenyek Az egyetemi programozói versenyek története Magyarországon 1990-ben kezdődött egy a Budapesti Műszaki Egyetemen (BME) rendezett versennyel, ahol nemcsak a BME csapatai, hanem a fóvárosi egyetemeken kívül vidéki csapatok is indultak. Ezen a versenyen a KL TE 3 csapatot indított, melyek a középmezőnyben végeztek. 1991 után minden évben a fóvárosban és Debrecenben is rendeztek versenyeket. Ezeken a versenyeken az induló csapatok száma nagyjából állandó volt: Budapesten kb 25, míg Debrecenben kb 10. 3
  5. 5. A versenyek tapasztalataiA következő néhány mondatban a versenyeken, a problémák megoldásával kapcsolatban szerzetttapasztalatokról szeretnék írni. A verseny kezdetén érdemes minden feladatot átolvasni és értelmezni.Az egyes csapatokon belül többféle megoldási módszer is kialakulhatott, az egyik lehetséges, hogy azértelmezés után a csapattagok egymás között szétosztják a feladatokat és eztán egyenként, vagyproblémás feladat esetén együtt keresik a megoldást és valósítják meg a kivitelezést. A másikmódszer, hogy minden feladat elvi megoldását a csapattagok együtt keresik, csak a konkrétmegvalósítás ideje alatt dolgoznak különböző feladatokon a csapattagok. De ezektől különböző másmódszerek is kialakulhattak, valószínűleg erre nincs általános alkalmazható stratégia. A feladatokmegoldásakor szerenesés esetben, amikor egyszerre több feladat elvi megoldása is elkészült, a szűkkeresztmetszetet a rendelkezésre álló egyetlen számítógép gépideje (5 óra) jelenti. Érdemes a legkönnyebb, legegyszerűbb probléma megoldásának megvalósításával kezdeni. A megoldásokbannem kell szépségre és az eleganci ára törekedni, mert a zsűri ezt nem értékeli. Sokkal inkább a kód egyszerűségére és átláthatósága kell, hogy a cél legyen. A feladatok elég nagy része többféleképpen is megoldható. Azoknál a feladatoknál, ahol a megoldást el lehet érni a feladatbeli objektumok összes esetének (pl. összes permutáció) vizsgálatával, ott a program rendelkezésére álló 1 perces futási időre kell figyelnünk, azaz tisztában kell lennünk az átlagos PC-k (különösen a zsűri által használt PC) .:» gyorsaságával. Viszont ha az összes eset vizsgálata belefér az egy perces futási időbe, nem érdemes a szép és "eszes" megoldás megkeresésévei foglalkozni. A későbbiekben az adott helyen az egyes algoritmusok futási idejére utalni fogunk.A mellékletben található feladatokA mellékletben található feladatokra a dolgozatban a feladatok azonosítójával hivatkozunk, amelyXXXXéé-n alakú, ahol XXXX a verseny helyszínének rövidítése, éé a verseny megrendezésénekévszáma évszázad nélkül, n pedig a feladat sorszáma. A mellékletben a feladatok évszám szerintnövekvő sorrendben találhatók. A formátumuk eltér az eredetitől, hogy egységesen kezelhessük őket.A legnagyobb része a feladatoknak négy jól körbehatárolható csoportból kerül ki. Ezek akövetkezőek:1. Szimulációs feladatokEzekben a feladatokban jól definiált objektumokkal találkozhatunk, amelyekhez szabályok tartoznak.Az objektumok ezen szabályok szerint viselkednek. A megoldáshoz nem kell egyéb, mint a feladatpontos megértése, az objektumok megfelelő gépi reprezentál ása és a szabályok pontos programozása. Könnyebb szimulációs problémák: USSC85-3, KLTE91-1, RUGB91-1, RUGB91-2, ACMF91-5, IOAG91-1, ODUN92-1, RUGB92-2, RUGB92-5, KLTE92-2, USEC92- 2, ACMF92-1, KLTE93-4 Nehezebb szimulációs problémák:KLTE91-2, RUGB91-5, RUGB91-7, ACMF91-3, ACMF91- 4, RUGB92 -6, TUBP92-1,A szimulációs feladatok megoldásával a dolgozatban nem foglalkozunk. Megoldásukat a Pascalnyelvvel ismerkedőknek ajánlhatjuk. Megoldásukhoz csak alapvető matematikai ismeretekre vanszükség. 2. Kombinatorikai feladatok lásd a dolgozat első fejezetét 3. Geometriai feladatok lásd a dolgozat második fejezetét 4. Gráfelméleti problémák lásd a dolgozat harmadik fejezetét. 4
  6. 6. Az egyes versenyek helyszínei és időpont ja:Rövidítés Színt Helyszín Időpont (forduló)USSC85 2 ?, California , USA 1985.KLTE91 1 KL TE, Debrecen 1991. JúniusIOAG91 * Athen, 3rd International 1991. Május Olympiad in InformaticsTUBP91 1 BME, Budapest 1991. OktóberRUGB91 2 Gent, Belgium 1991. NovemberACMF91 3 USA 1991.USSC92 2 ?, USA 1992.ODUN92 1 Norfolk, Va USA 1992. SzeptemberTUBP92 1 BME, Budapest 1992. OktóberKLTE92 1 KL TE Debrecen 1992. OktóberRUGB92 2 Gent, Belgium 1992. NovemberACMF92 3 Indíanapolis, USA 1992.KLTE93 1 KL TE, Debrecen 1993. Szeptember 5
  7. 7. 1. Kombinatorikai algoritmusok1.1. Ismétlés nélküli permutációkVizsgáljuk először az ismétlés nélküli permutációk generálásának problémakörét. A probléma pontosdefmíciója a következő:Adott a P={ 1,2 ..n} halmaz, előállítandóa) az összes permutációja tetszőleges sorrendben.b) az összes permutáció ja lexikografikus sorrendben.c) a lexikografikus rendezés szerinti i-edik permutációjaA P összes permutációinak halmazát jelöljük P! -al : P! = {~, pz o 00 Pn! }Először az egyik legegyszerubb módszer bemutatásával kezdjük. A módszert Fike publikálta 1975-ben [Fike1975] , majd 1976-ban Rohl módosította [Roh1l976] .Legyen S={(d2,d3, ••• ,dn) II~dk s k: k=2,3, .. n} ekkor S összesen 2*3* ..*n=n! vektorttartalmaz. Vegyük észre, hogy S elemeit programmal könnyen lehet generálni: kis n esetén n-l darabegymásba ágyazott ciklussal, ahol a ciklusváltozók értékei rendre az [1..2], [1..3], ... ,[1..n]intervallumokat futják be, nagy n esetén rekurzívan. Ha egyszeruen programozható egy-egy értelműmegfeleltetést adnánk S és P! elemei között akkor, mivel S elemeit könnyen generálhatjuk egyszerumódszert kapnánk P! generálására. A Fike módszere a következő egy-egy értelmű megfeleltetést adjaS és P! elemei között: Legyen (d2, d3, ••• , dn) egyelem S-ből, ekkor a hozzátartozó P permutációtúgy kapjuk, hogy kiindulva ~ = (1,2, ... , n) - ből, mint kezdeti permutációból cseréljük fel P; -ben ak-adik elemet a dk -adikkal.Mindezek alapján az algoritmus először az S-beli elemeket generálja, majd ebből állítja elő a fentleírtaknak megfelelően a kapcsolódó permutációt.Észrevehető, hogy a fenti algoritmus redundáns elemeket tartalmaz (pl ha dk = k, akkor feleslegescsere), ezen elemek kiküszöbölésére tett módosítást 1976-ban J. S. Rohl. A Rohl által módosítottalgoritmus pascal programja a következő:proeedure Fike_Rohl_perm(n:integer);var p:array[l ..max) of integer; { n <= max} i:integer;proeedure permute(k:integer);var temp, dk, dn:integer;begin if k=n then begin proe (p) ; temp: =p [nl ; for dn:=n-l downto 1 do begin p[n) :=p[dn) r p l dn ) :=temp; p.r t p j r oc p j dn l r=p I n l r end; p[n) :=temp; end else begin permute (k+l) ; temp :=p [k) ; for dk:=k-l downto 1 do begin p [k) :=p [dk) ; 6
  8. 8. p[dk] :=tempi permute(k+l)i p [dk] :=p [k] i e rid r p[k] :=tempi endi endi be gin {Fike_Rohl perm} for i:=l to n do p[i] :=ii permute(2)i endi Fig. 1.1.1.. Fike algoritmusa Rohl módosításaivalA versenyfeladatok megoldásánál a közölt eljárás általában jól használható, de azokban aproblémákban ahol a feladat szempontjából n állandó és n nem túl nagy (n<6), elképzelhető olyaneljárás is amely n darab egymásba ágyazott ciklust tartalmaz az S elemeinek generálásához. Ennekaz algoritmusnak a gyorsaság mellett a kód egyszerűsége is az előnye.A fenti módszer csupán az (a) problémára ad választ. Ha pl. valamely feladat a lexikografikussorrendben követeli meg tőlünk a permutációk felsorolását, akkor a módszerek egy újabb családjávalkell megismerkednünk [We1ll971] . Most egy olyan módszert mutatunk be, amely 1812-bőlszármazik, első említése [FiscI812] majd [ShenI962] . A módszer lényege négy lépés alkalmazása =egy adott P (Pl P2 Pn) permutációra, amely eredményeként a lexikografikusan következöpermutációt kapjukA négy lépés:(1) Legyen i a legnagyobb index, amelyre p i-l< Pi(2) Legyen} az a legnagyobb index, amelyre Pi-I<Pj(3) Cseréljük fel Pi-l -et p.-vel.(4) Fordítsuk meg Pl Pi+I,··.,Pnsorrendjét. Fig. 1.1.2 ..A lexikografikus felsorolás négy lépésePl. 1.1. Írjunk olyan pascal programot, amely az 1.1.2 ábra alapján lexikografikus sorrendbengenerálja egy halmaz partícióitMost egy másik, a permutációkat lexikografikusan felsoroló módszer mutatunk be. P! egy általánoselemének generálásakor az összes N={l,2 ..n} számnak hozzá kell rendelődnie a Pl -hez, majd azN {Pl} -beli összes elemnek hozzá kell rendelődnie a P2-höz, és így tovább. Ezek alapjánalgoritmusunk szerkezete a következő: Ahhoz, hogy a permutációkat lexikografikus sorrendbenkapjuk korlátoznunk kell az egyes Pj -k kiválasztásának sorrendjét. Ha a választható elemek közülelsőként mindig a kisebbiket választ juk, akkor a permutációkat lexikografikusan növekvő sorrendbenkapjuk.Kézenfekvő, hogy algoritmusunkban a választható elemeket egy listában tároljuk, a listák kezelése(elem-törlés, -beszúrás, stb.) a pascal nyelvben a jól ismert mutatós módszerrel talán túl sokadminisztrációs lépéssei járna, ezért kihasználva a jelen probléma specifikurnát a megfelelő listát egytömbbel szimuláljuk, legyen ez a:array[O ..n} o/integer. A tömb egy elemének indexe reprezentáljai-edik listaelem által tárolt értéket, míg maga az elem a lista következő elemére mutat. Vagyis pl. az[1,3,3,4,6,6,0] tömb az 1 ~ 3 ~ 4 ~ 6 listát reprezentálja. 7
  9. 9. Ekkor az a[O..n] inicializálását, feltöltését a következő eljárás végzi:procedure Init;var i: integer;begin for i:=O to n-l do a[i):=i+l; a[n):=O;end;Tegyük fel, hogy p[1..n] egy globális tömb var p:array[1 ..n} of integer defmícióval, a permutációktárolásához, valamint már létezik a PrintPerm eljárás a kész permutációk megjelenítéséhez. Ekkor apermutációkat lexikografikusan felsoroló algoritmus pascal kódja a következő:procedure enum(i:integer);var t:integer;begin t:=Oi while a[t)<> O do begin p[i]:=a[t]; if i<> n then begin a [t] :=a [a [t]]; enum(i+l) ; a [t] : =p [i] ; end else Printperm; t:=a[t]; end; Fig. J. J. 3.: Permutációk lexikografikusanAz algoritmusunknál alkalmazott gondolatmenet, mint majd látni fogjuk ismétléses permutációkra isáltalánosítható lesz.A fenti algoritmusok teljesítménye között a versenyfeladatok megoldásának szempontjából lényegeskülönbség nincs. Ezen azt kell érteni, hogy a megoldások a futásra felhasználható idő (1 perc) alattnagyjából n=ll-ig képesek az összes permutációt előállítani. Természetesen a fenti módszerekenkívül számos más módszer is ismeretes, melyek más-más célra használhatók a legalkalmasabban. Akülönböző algoritmusok több szempontú összehasonlításával foglalkozik Roy [RoyI978] és Ives[Ivesl976].A probléma (c) részében megfogalmazottakra mind Fike [FikeI975], mind Wells [WeIIsi971] kínálmegoldást. A (c)-ben megfogalmazott probléma speciális esete (n=k) annak a problémának amikoregy n elemű halmaz k-ad osztályú kombinációinak összes permutációit rendezzük lexikografikusan ésezek között keressük a i-ediket. Ennek az általánosabb problémának a megoldása a "Kombinációk"című fejezetben található.A (c) problémára ezen kívül hasznos eligazítást találhatunk [BrowI970] -ban is.Irodalomjegyzék az 1.1. fejezethez [FikeI975]: C. T. Fike (1975). A permutation generation method. The Computer Journal, Vol. 18,p21. [Rohll976]: 1. S. Rohl (1976). Programming improvements to Fikes algorithm for generating permutations. The Computer Journal, Vol. 19, p 156. 8
  10. 10. [WeIll 1971]: M. B. Wells (1971). Elements ofCombinatorial Computing. Pergamon Press, NewYork[Fisc1912]: L. L. Fischer and K. Chr Krause (1812). Lehrbuch der Combinationslehre und der Arithmetik. Dresden.[Shen1962]: Shen, Mok-Kong (1962). BIT Vol. 2. p. 228.[RoyI978]: M. K. Roy. (1978). The Computer Journal, VoI2l., p. 296.[Ivesl976]: F. M. Ives. (1976).Permutation Enumeration: Four New Permutation Algorithms CACM, Vol. 19., Nr. 2., p. 68.[Brow 1970]: R. M. Brown: Decoding Combinations of the First n Integers Taken k at a Time. CACM Vol. 3-4 p 235. 9
  11. 11. 1.2. Ismétléses permutációkA probléma pontos definíciója a következő:Adott 1 <= r <= n pozitív egész (n darab, r különböző elem permutációit keressük), rvalamint azF = (ft .t; ···,fr) vektor, ahol n = LJ; és 1::;; J; (i = 1,2, ... r) ;=1generálandó az M = {u,..,1,2,2, .. ,2, .... ,r,r, .. ,r} ~ ~ ---v--- ft h /,halmaz összes (ismétléses) permutációja.Egy ilyen permutációt jelöljünk csakúgy, mint az előzőekben P = (Pl P2"" Pn)-vel.A P-t generáló algoritmusunk egybeesik azzal a módszerrel ahogyan "kézzel" felírnánk a fentipermutációkat: Válasszunk M-ből egy elemet az összes lehetséges módon ez lesz Pl minden egyesilyen választás után válasszunk egy elemet M{Pl}-ből P2-helyére, ..., és végül minden egyes Pn-lkiválasztása után Pn helyére válasszunk M {Pl P2"" Pn-l} -ből, mint az előző fejezetben. Ha r=nakkor az ismétlés nélküli permutációkat kapjuk. Ha az összes lehetséges módon történő választástaz elemek növekvő sorrendjében végezzük el, akkor a permutációkat is lexikografikusan ebben asorrendben kapjuk. Rohl 1978-ban publikálta [Roh1l978] a fenti módszert némi általánosítással: Haaz algoritmus során a kiválasztásokat nem végezzük el csak Ps -ig (S < n )-ig akkor n-elem s-edosztályú kombinációinak (ismétléses) permutációit (s-permutációit) kapjuk csakúgy, mint az előzőfejezetben.Végül algoritmusunk programja a következő:procedure genperm(m,f:vect;r,rO:integer);const max=20;type vect=array[l ..max] of integer;var p:intvect; k:integer;procedure choose(k:integer);var i:integer;begin for i:=l to r do if f[i] <> O then begin p [k] : =m [i] ; dec(f[i]); if k<>rO then choose(k+l) else proc(p); inc(f[i]); endend; begin {genperm} choose(l); end; Fig. 1.2.1.: Rohl algoritmusa (1978). 10
  12. 12. Ha valamely n elem ismétléses permutációi közül a lexikografikus sorrendben pontosan az i. -re vansziikségünk akkor az ezt előállító algoritmust Wells [Welli971] munkájában találjuk. Készítsük mostel az i-edik lexikografikus ismétléses permutációt generáló algoritmus saját verzióját. Kiinduláskéntalkalmazzuk Wells az "inverzfeladat"-ot megoldó algoritmusát [We1ll971] . Ez az algoritmus azinputjaként egy permutációból előállítja az adott permutáció lexikografikus sorrendbeli sorszámát:const max=100itype intvect=array[O ..max] of longintifunction nalatt k (n:longintik:longint) :longinti Var i :integer i result:longinti Begin result:=li if k<>O then for i:=O to (k-1) do result:=(result div (i+1) )*(n-i)i nalatt k:=result endi function iperm2num(n,r:integerif,p:intvect) :longinti (* osszesen k-1 fele objektumunk van, osszesen n darab objektumunk van, n=f [O]+f [1]+ ...+f [r-1] f[j]: a j. objektumból f[j] darab van O <= j <= r-1 *) var H,MM,J:intvecti q,i,jj:integeri nn,v:longinti begin for i:=O to r-1 do begin h[i] :=OiMM[i] :=Oij[i] :=1 endi for i:=O to n-1 do begin MM[p[i]] :=MM[p[i] ]+n_alatt k(h[p[i]],j [p[i]]) i inc(h[p[i]]) iinc(j [p[i]]) i for q:=O to p[i]-l do inc(h[q])i e nd r v:=liNN:=Oiq:=f[r-1]i for jj:=r-2 downto O do begin NN:=NN+MM[jj]*Vi q:=q+f[jj]; v:=v * n_alatt k(q,f[jj]); end; iperm2num:=NN endi Fig. 1.2.2.: Wellsféle JPERM2NUM foggvény (1971). II
  13. 13. A mi feladatunk azonban olyan algoritmus írása, amely a sorszám alapján "legyártja" a hozzátartozó (ismétléses) permutációt.Vegyük észre, hogy adott M és resetén M bármely MO részhalmazának a lexikografikusan első(jelöljük [MO]F-al) ill. lexikografikusan utolsó permutációja (jelöljük [MO]L-el) egyszeruenmegadható az elemek sorbarendezésével.Keressük tehát a M lexikografikusan K-adik permutációját P-t. Próbáljuk megkeresni Pl-et P elsőbetűjét, ekkor PI-re:ipemünumcn -1, r, f, [{MPI }]L ) =< K,ahol f ,[;] .= {fU] - 1, ha j = Pl ,(; .= . . . " 1,2, ...r) (1) f[;], egyébként r = {r :-1, ha f[PI] =1 r , egyébkén!és nyilván Pl az a szimbólum amelyre iperm2num(n-l,r, f, [{MPI}]L) maximális (1)tulajdonságú.Pl után P2-t mint n-1 darab és r különböző szimbólum lexikografikus sorrendben vettK - perm2num(n -1, r, f, {M Pl}) sorszámú permutációjának első betűjeként keressük ..és így tovább egészen Pn-ig. Legyen az ezt megvalósító pascal kód megírása ismét az olvasó feladata!P 1.2.1. Írjunk olyan pascal függvényt, amely az előzőek alapján generálja a K. sorszámhoz tartozólexikografikus permutációját valamely M halmaznak !Valamely halmaz ismétléses permutációit előállító algoritmust találunk még [Barti967] -ben és[SagI964]-ben is.Kapcsolódó versenyfeladatok:Feladat InstrukcióUUSC85-2 Az összes esetek száma (kb. 9!) lehetővé teszi, hogy egyenként megvizsgáljuk őket A megadott öt szám összes permutációit (5!) vizsgáljuk, az összes lehetséges müveletjelezéssel (44).TUBP91-4 Az egyenlő számjegyek elhagyása után az összes esetek száma 10!RUGB92-1 Ismétléses permutációk lexikografikusanRUGB92-4 Mivel a gráf csúcsainak száma nem több mint 8, ezért a csúcsok összes lehetséges sorrendjét megvizsgálva (8!) a minimálisat bizonyosan megtaláljukACMF92-2 A hálózatba kapcsolt gépek maximális száma 8, ezért az összes eset vizsgálataKLTE93-3 lehetséges.Irodalomjegyzék az 1.2 fejezethez [Rohll978]: J. S. Rohl. (1978). Generating permutations by choosing. The Computer Journal, Vol 21., p 303. [we1ll971 ]: M. B. Wells (1971). Elements ofCombinatorial Computing. Pergamon Press, New York [Bratl967]: P. Bratley (1967).Permutations with repetitions. CACM Vol. 10. p. 450 [Sag1964]: T. W. Sag (1964) Permutations of set with repetitions. CACM Vol. 7. p 585. 12
  14. 14. 1.3. KombinációkEbben a fejezetben a feladatunk n-elem r-edosztályú ismétlés nélküli kombinációinak generálása,úgy, a generált kombinációk sorrendje is számít. Ahogy már az ismétlés nélküli permutációkkalfoglalkozó fejezetben utaltunk rá, ez a probléma az ismétlés nélküli permutáció generálásáltalánosításának tekinthető.Most az előző fejezetekkel ellentétben csak a legáltalánosabb eljárást mutatjuk be. Nem foglalkozunka "rendezettlen" kombinációk generálásával. A lexikografikus algoritmusok közül is csak azzalfoglalkozunk, amely a lexikografikusan t. kombinációt fogja közvetlenül előállítani.Keressük praktikusan a Z; = {l, 2, .... , n} halmaz r-edosztályú kombinációinak összes permutációit,vagyis a Pn.r = Pl>P2"" Pr ,ahol Pl>P2" .,Pr E {l, 2, ... ,nl és Pi :j:; P, ha i :j:; j. vektorokat.Ezek halmazát jelöljük P(n, r)-el. Nevezzük Pn. r = Pl> P2" Pr -t a rövidség kedvéért el egy r-permutációnak !Legyen P; r = Pl P2"" Pr egy r-permutáció, ekkor Pn. r inverziója a Cn r = C1 , C2,···, Cr vektor,aholCi E {O, 1, .... ,n-i} és r-i Ci = Pi - i+ Lo i, j j=lahol o = {O,hap.<p. J i.j 1, ha P, > PiEgy Pn. r-hez tartozó Cn. r előállítása a fenti képIetet követve meglehetősen egyszerű, dekihasználhatjuk a Pn.r és Cn.r kapcsolatának egy speciális tulajdonságát. Nevezetesen, hogy ha Z;elemeit a már l.l-ben megismert listában tároljuk, akkor c1-et úgy kapjuk, hogy megszámláljuk,hogy ebben a listában hány elem előzi meg Pl -et, majd Pl -et töröljük a listából, c2 értékénekmeghatározásához az így nyert listában meg kell számlálnunk a P2 -t megelőző elemek számát, majd P2 -t is töröljük a listából a többi Ci -t ugyanilyen módszerrel kapjuk:proeedure eodingivar i,t, eount:integeribeginiInit;for i:=l to r dobegin eount:=O; t:=O; while p[i)<>a[t) do begin t:=a[t); ine(eount) end; eli] :=eounti a [t) :=a [a [t) )end; Fig. 1.3.1.: Permutációk inverziójánakgenerálása 13
  15. 15. A Cn, r ~ Pn, r átalakítást végző eljárás szintén a fenti tulajdonságot használja ki:procedure Decoding;var i,j,t:integer;begin Init; for i:=l to r begin t:=O; for j :=1 to c[i] do t:=a[t]; p [i] : =a [t] ; a [t] :=a [a [t] ]; endend; Fig. 1.3.2.: A C-fP átalakítást végző eljárás.Meg kell még jegyeznünk, hogy lexikograftkusan kisebb r-permutációhoz lexikografikusan kisebbinverzió fog tartozni, vagyis, hogy a Cn,rBPn.r megfeleltetés, ilyen értelemben rendezés tartó.A lexikografikusan t. r-permutációt közvetlenül előállító rPermGen nevű eljárás egyegy-egy értelműmegfeleltetés a Pen, r) és a Z = {l,2, ...,IP(n, r)l} halmazok között. Konstruáljuk meg előszörrPermGen inverzét, vagyis azt a RankrPerm nevű eljárást amely lexikografikus sorrendbenmegsorszámozza Z; r-permutációit.RankrPerm konstruálásához felhasználjuk a fent bevezetett inverziók és P(n,r) egy-egy értelmű =kapcsolatát. Cn, r definíciójából látható, hogy egy cp cz, ... , c -vel kezdődő Cn. r CI Cz, ... , Ci>"" Cr jinverziót pontosan [Pm-i, r-i)1 olyan inverzió előz meg, amelyeknek c ,c Z , •.• ,c előtagjára az igaz, I l jhogy clj=cj j=I,2, .. ,i-Iéscl <c j ugyanis, a c +l végigfut ja a [O,l, ..,n-(i+l)] intervallum j jértékeit, C +Z pedig [O,1,..,n-(i+2)] intervallum on fut végig, és így tovább. Ekkor az így összeszámolt jesetek száma:(n-i)*(n-(i+l))* ... *(n-(r-2))*(n-(r-l))= (n-i)! = IP(n-i,r-i)1 O (n-r)!Vagyis a RankrPerm eljárás a következő lesz:Procedure RankrPerm(c:codeword, var Rank:integer);var i:integer;begin Rank:=l; for i:=l to r do Rank:=Rank+c[i]*P(n-i, r-i);end; Fig. 1.3.3.: A RankrPerm eljárás.A hivatkozott P(n,k) függvény definíciója a pontosság kedvéért a következő: nlP(n,k) := . (n-k)!A RankrPerm eljárás az adott r-permutáció inverziójához rendeli a kívánt sorszámot, vagyis egyadott r-permutáció esetén RankrPerm hívását meg kell előzze a Coding eljárás hívása.Az rPermGen eljáráshoz a tulajdonképpen már l.2-ben is alkalmazott trial-and-error módszeralkalmazásával jutunk. A módszert most is az inverzfüggvényre (a RankrPerm eljárás) alkalmazzuk: 14
  16. 16. procedure rPermGen(rank, k:integer; var c:codeword);var i:integer;be gin for i:=n-k downto O do if rank > i*p (n-k, r-k) {Trial} then begin c[k):=i; if k <= r then RankrPerm(rank-i*P(n-k, r-k),k+l); exit; endend; Fig. 1.3.4.: Az rPermGen eljárás.Azonos kiindulás után kissé eltérő gondolatmenetet találunk még [Knot1976]-ban a problémára. Azeredeti problémához kapcsolódó feladatokat találunk még [Welll971] -ben.Kapcsolódó versenyfeladatok:Feladat InstrukcióKLTE91-3KLTE92-1 Lexikografikus kombinációk felsorolásaIrodalomjegyzék az 1.3 fejezethez[Knot197 6]: G. D. Knott (1976). A Numbering System for Permutations ofCombinations. Communications of ACM, Vol 19, p 355.[We1ll971]: M. B. Wells (1971). Elements of combinatarial programming. p 130. 15
  17. 17. 1.4. Egy halmaz összes részhalmazai, particionálásaTegyük fel, hogy az a feladatunk, hogy egy halmaz (praktikusan az {1,2, ... ,n} halmaz) összesrészhalmazait kell generálnunk lexikografikus sorrendben.A fenti halmaz részhalmazainak reprezentációja legyen a következő:Minden egyes részhalmazt jelöljön egy f!. = ~ ~ ... ar számsorozat, úgy, hogy~ < a2 < ... < ar ís r :s;n, ahol ~ ~ ... ar az adott részhalmaz elemi. Könnyen látható, hogy eza jelölés egyértelmű.Definiáljuk most, az egy halmaz részhalmazainak halmazán értelmezett a lexikografikus rendezést:Legyen f!.= ~ ~ ... = ap és f l1 <; ... cq két részhalmaz,akkor ha létezik olyan i (l :s; i :s; q) amelyre minden 1 :s; j :s; i esetén aj = cj és vagy ai < Ci (1) vagy p=i-1 akkor azt mondjuk, hogy ~ lexikografikusan megelőzi (kisebb, mint) c-t.Mindezek alapján pl. n=4 esetén a fenti halmaz részhalmazai lexikografikusan növekvő sorrendben akövetkezők: 1,12,123,1234,124,13,134,14,2,23,234,24,3,34,4A fenti halmaz részhalmazait n-jegyű bináris számokkal is reprezentálhatjuk: A 1 b2 ••• bn pontosanakkor tartozik az A részhalmazhoz, ha bi = 1 <=> i E A (i = 1,2, .. , n)Vegyük észre, hogya részhalmazokon értelmezett lexikografikus rendezés nem ugyanazt a sorrendetadja, mint az őket reprezentáló bináris számokon (bit-sztringeken) értelmezett lexikografikusrendezés.Ha valamely feladat a részhalmazok felsorolásán kívül, nem követeli meg tőlünk a lexikografikusrendezettséget, akkor az {1,2, ...,n} halmaz részhalmazainak felsorolása binárisreprezentációjukban, nem más, mint az egész számok bináris alakjainak felsorolása. O-től 2n-l-ig.Egy szám bináris alakjának keresésekor kihasználhatjuk, hogy a Pascal a Word típus esetén aszámokat éppen a nekünk megfelelő formában tárolja.Egy kicsit bonyolultabb probléma az (l)-el defmiált rendezés szerint a részhalmazok felsorolása, eztvalósítja meg a következő program:Program Subset_enumeration;Const n=13;{ <n> elemű halmaz részhalmazait soroljuk föl lexikografikusan A program ebben a sorrendben bármely t.-ediket képes generálni (két részhalmaz akkor különbözik, ha van különböző elemük) A teljesítményről: 486SX25-on n=13 esetén az összes részhalmaz felsorolása kb 40 másodpercet vesz igenybevar t:longint; i,r:integer; b:array[l ..n] of integer; {ha az b[i]>O akkor az i-edik elem a reszhalmazban van} a:array[l ..n] of integer; {az elso r eleme nem mas, mint a t. reszhalmaz } function kettoad(x:integer) :longinti var c:longint;i:integer; be gin c:=l; for i:=l to x do c:=c*2; Kettoad:=c end; 16
  18. 18. procedure subset(t:integerivar r:integer)ivar k:integerih:longintibegin for i:=l to n do b[i] :=Oi r:=Oi k:=li repeat h:=kettoad(n-k)i if t<= h then begin b[k] :=li inc(r)i arr] :=k endi t:=t-(l-b[k])*h-b[k]i inc (k) until ((k>n) or (t=O)) endibegin for t:=l to kettoad(n) do begin subset(t,r)i writelniwrite(t, :)ifor i:=l to r do write(a[i], li endend. Fig. 1.4.1.: Az {1,2, .... ,n} halmaz osszes részhalmazának felsorolása.A fenti program az eredeti célkitűzést általánosítva alkalmas arra, hogy az {1,2, ... ,n} halmaz az (1)szerinti rendezésben t-edik részhalmazát generálja. A program subset eljárása hívás után az a, bglobális változóban t-edik részhalmazt, az r-paraméterében pedig a t.-részhalmaz elemszámát adjavissza.Legyen Z; = {I, 2, .... , n}. Ekkor a Z; halmaz egy partíciója az azon halmazokP = {7rl 7r2, ••• , 7rm} halmaza, amelyre 7ri n 7rj = 0, ha i1:. j és 7r 1:.0, i=I,2, ... ,m 1 (2 ) m és Unt i=l = Z;feladatunk rögzített n esetén az összes fenti tulajdonságú halmazrendszer előállítása.Számítsuk először ki, hányféle különböző particionálása létezik az {1,2, ... ,n} halmaznak !Jelölje Bn a keresett partíciók számát (Bell-féle szám), ekkor n= 1 esetén nyilván B 1= 1Tegyük fel, hogy Bl, B2, ••• ,Bn_l ismert. Ekkor Hn_l-hez vegyünk egy a többitől különböző elemet,jelöljük ezt a-val. Keressük meg H; = Hn_ {a} összes partícióit ! l UVilágos, hogy azon partíció száma, amelyekben {a} szerepel B n-l hiszen ezeket úgy nyerhetjük,hogy Hn_l-minden partíciójához hozzávesszük a {a} halmazt. Továbbá az {a, p}-t (p EHn_l)tartalmazó partíciók száma, egy rögzített p -esetén Bn_2, mivel p-t összesen (n~l)-féleképpenválaszthat juk ki, ezért azon partíciók száma, amelyekben ex, egy kételemű részhalmazba esik 17
  19. 19. ( n~l) . Bn_l és ugyanígy gondolkozva azoknak a partícióknak a száma, ahol a. egy i-eleműrészhalmazba esik (~~:). Bn_iVagyis H; = Hn- 1 u {a} összes partícióinak száma:B n = n~. 1=1 (n-l) 1- n (n-l) n-I 1 B n-m = 1=1 n - 1. B n-l.= k=O L L (n-l) k BkA táblázat Bn értékeit mutatja: 12 4213597 Fig. 1.4.2.: s; értékeiA következőkben M. C. Er 1987-ben publikált [MCER1987] módszerét mutatjuk be.Legyen a C = llC2 ••• cn kódszó a Z; egy partícióját leíró kódszava, úgy, hogyn=4 esetén a következő táblázat mutatja a kódszavakat: Kódszó 1111 1112 1121 1122 1123 1211 1212 1213 1221 1222 1223 1231 1232 1233 1234 Fig. 1.4.3.: Az {1,2,3,4} halmaz összes partíciói és a hozzájuk tartozó kódszavak.A táblázatból is látható, hogy ebben a kódszó konstrukcióban 1 ~ Ci ~ i. Másszóval i E Z; nemkerülhet 1ttbe, ha j > i. A következőkben azt vizsgáljuk, hogy a Z; halmaz összes partícióinakkódszavai hogyan vezethetők le a Zn_l halmaz összes partícióit leíró kódszavak sorozatábólhozzáadva cn -t a már meglévő kódszavakhoz. cn értékei az 1,2 ... ,max( ll, C;... Cn-1) + 1 intervallumértékei közül kerülnek ki.Ezen tulajdonságok segítségével működik 4.4 algoritmusunk, amely a megfelelő kódszókat generálja.A hivatkozott SP(m, p) eljárás definíciójában az m = max( eJ, ~ ... Cn_l) és p paraméter adja azaktuális kódszó aktuális jegyét. Eljárásunk feltételezi a PrintPartition eljárást, amely a kódszó B partíció konvertálást ill. az így nyert partíció megjelenítését végzi. Mindezek után eljárásunk akövetkező: 18
  20. 20. type codeword=array[l ..max] of integeriprocedure SetPartitions(n:integer)ivar c:codewordiprocedure SP(m,p:integer)jvar i:integeri begin if p > n then PrintPartition(c) else begin for i:=l to m do begin c[p] :=iiSP(m, p+1) i endi c[p] :=m+1i SP(m+1, p+1) end endibegin SP(O, 1)endi Fig. 1.4.4.: M C. Er rekurzív algoritmus az {l.2 ....•n} halmaz összes partíciójának generálásához.A fenti Pascal kód Borland Pascal 7.0-ás verziójú fordítót használva napjaink átlagosnak mondhatóteljesítményű PC-jén (486 SX 25) a következő futási időeredményeket adta: n Futási idő 10 < 1 sec 11 < 4 sec 12 < 21 sec 13 < 130 sec Fig. 1.4.5.: Az Er-féle rekurzív halmaz particionáló algoritmus futási ideje néhány n-re.Mint a táblázatból is látható, a versenyfeladatok esetén akkor alkalmazható az algoritmus hafeldatbeli n < 13.Ha az eredeti problémát, úgy módosít juk, hogy csupán a lexikografikus sorrendben t. partíciótkeressük, akkor a fenti Er-féle algoritmus nem használható hatékonyan, hiszen ahhoz, hogymegkapjuk a a t. partíciót le kell generáltatnunk a megelőző t-l darab partíciót is.A lexikografikusan t. partíciót előállító algoritmust megkaphatjuk, ha megfeleltetést találunk atermészetes számok és a már ismertetett konstrukcióval előállított kódszavak között.Az említett megfeleltetés bemutatásához vezessük be a következő jelöléseket:Dn(r, d) :azon CIC2,,,,Cn kódszavak száma, ahol CIC2,,,,Cr rögzített és d = max(c ,cl 2, •. ,cr_l) (r=2, ... ,n+1; d=1, .. ,r-1)Könnyű látni, hogy mivel minden kódszóra igaz, hogy CI = 1 ezért D; (2, 1) = En, továbbá az is igaz,hogy Dn(n+l,d)=l (d=l, 2, ... n) ,valamint Dn(n,d)=d+l (d=l, ... ,n-l).Határozzuk meg D; (n -1, h) -t! 19
  21. 21. Ekkor Cl ,C2,··· ,cn-2 ,Cn-l ,Cn ----v------ max=h (1)vagyis cn_l helyére 1,2,,,.,h, h+ 1 kerülhet, ezek közül 1,2,,,.,h úgy, hogy maxfc, ,c2 oo,cn_l) = higaz marad, vagyis az ilyen kódszavak száma h* Dn(n, h),ha cn-l = h + 1 (azaz cn_l-et is rögzítjük) , akkor az ilyen ( 1) tulajdonságú kódszavak számaDn (n, h + 1), vagyis összesen: (2)Hasonló okoskodással, kapjuk, hogyDn(r, d) = d * Dn(r + 1, d) + Dn(r + 1,d + 1) (r = 3,oo,n -1; d = l,oo.,r -1) (3)Vagyis D; (r, d)értékei könnyen kiszámíthatók. Ezek alapján a sorszám ~ kódszó konverzíót végzőalgoritmust könnyű elkészíteni:procedure rank(t, n:integer; c:codeword)ivar r:integer;begin t:=l; d:=l; for r:=2 to n begin do t:=t+([r]-1)*Dn[r+1, d]; if c[r]> d then d:=c[r] endend;Az eljárás feltételezi, hogya Dn[2"n+ 1, 2"n+ 1] tömbben e D; (r, d) megfelelő értékei vannak.Az inverz eljáráshoz, vagyis a kódszó ~ sorszám konverzióhoz, hasonlóan gondolkodva, mint az l.2fejezetben a következő algoritmust kapjuk:program Set_Partitionsiconst Bell:array [0..15] of longint= (1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147, 115975, 678570, 4213597, 27644437, 190899322, 1382958545 )i var n:integerit:longint;r,i,j:bytei Dn:array[1 ..18,1 ..18] of longinti C:codewordi procedure PreCalcD(n:integer); { A D(n) értékek kiszámítása} var d,r:integer; begin Dn [2,1] :=BELL [n] ; for d:=l to n-1 do Dn[n,d] :=d+1i for d:=l to n do Dn[n+1,d] :=1; for r:=n-1 downto 3 do for d:=r-1 downto 1 do Dn[r,d]:= d*Dn[r+1,d] + Dn[r+1,d+1]i 20
  22. 22. endiProcedure partition(tO:longint var d:byte)ivar r:integeri t,m,k:longintiBegin t:=tO id: =1 i for r:=2 to n do begin m r=O) repeat inc(m) until t <= m * Dn[r+1,d])i if d+1 < m then m:=d+1i c [r] : =mr t:=t-(m-1)*Dn[r+1,d]i if d < m then d:=mi endi endi begin writelni write( n:)i readln(n)i PreCalcD(n)i c[l] :=li for t:=l to BELL[n] do {a ciklus az összes partíciót generálja} begin partition(t,r)i {a t. partíció elő állítása} for j:=l to r do begin write(j,. reszhalmaz elemei:)i for i:=l to n do if c[i]=j then write(i, " )i writeln e nd r end end.A programpartition(t, r) eljárása generálja a lexikografikusan t. kódszót. Az eljárás a kész kódszót amár fent definiált codeword típusú C globális változóba teszi, míg a kódszóban leírt partíciórészhalmazainak számát az r-paraméterében adja vissza. A kódszó partícióvá alakítását a fóprogramvégzi. A fenti program egy adott n-hez tartozó {1,2, ...,n} halmaz összes partícióját előállítja úgy,hogy végrehajtja a partition(t, r) eljárást a t= 1,2,3, .... ,Bn értékekre.Az összes partíció generálását új algoritmusunk valamivellassabban végzi, viszont cserében a t.partíciót közvetlenül, az ezt megelőző t-I partíció előállítása nélkül állítja elő.A témakörhöz a következő feladat kapcsolódik:KLTE92-7A probléma neve: SzállításMivel a probléma csupán az n<100 korlátozást tartalmazza, ezért az összes eset generálása ésellenőrzése a fenti módszerek bármelyikévei is, nem valószínű, hogy megoldáshoz vezet akkor ha azsúri teszt inputadataiban a csomagok száma több, mint 13. Az előző korlátozás bevezetésévei 4.4algoritmusunkkal a csomagok halmazának összes partícióinak előállításával és ellenőrzéséveikiválaszthatjuk az optimálist.Az eredeti feladat megoldásához más módszert kell keresnünk !További kapcsolódó versenyfeladat: RUGB91-6 21
  23. 23. Irodalomjegyzék az 1.4 fejezethez[MCER1987]: M.C. Er (1987) Alghorithm for generating Set Partitions. The Computer Journal Vol. 31 No 3, pp 283 22
  24. 24. 1.5. Gray kódokA Gray kódok olyan k-bites bitminták sorozata, amelyek olyan tulajdonságúak, hogy az egymástkövetőek egymástól egyetlen bitben különböznek. Például a következő 3-bites Gray kódokalkalmasak arra, hogy a O-tói 7-ig a számokat kódolják: Gray kódok Sorszám Decimális érték 000 O O 100 1 4 101 2 5 001 3 1 011 4 3 010 5 2 110 6 6 111 7 7 Fig. 1.5.1.: Gray kódokA Gray kódok között speciális tulajdonságúak a Ciklikus Gray kódok, amelyekre a fentieken kívül azis igaz, hogy az utolsó és az első is egyetlen bitben különbözik egymástól. A fentiek nem CiklikusGray kódok. A továbbiakban csak Ciklikus Gray kódokkal foglalkozunk, ezért a rövidség miatt csakGray kódokként hivatkozunk rájuk. Gray kódok Sorszám Decimális érték 000 O O 001 1 1 011 2 3 010 3 2 110 4 6 111 5 7 101 6 5 100 7 4 Fig. 1.5.2.: Ciklikus Gray kódokItt megjegyezzük, hogy a Gray kódok O-val (minden bit O) kezdődnek és a következő sza.bállyalgenerálhatóak: Minden egyes lépésben az adott Gray kódból egyetlen bit negálásával apjuk akövetkező Gray kódot. Meg kell határoznunk ennek a bitnek a pozícióját. Erre a pozícióra pedig azigaz, hogy pontosan fele annyiszor kel1 változtatni, mint a tőle közvetlenül jobbra ál1ót. Ez a módszerviszonylag könnyen programozható, de megvan az a hátránya, hogyan-edik Gray kódot csak amegelőző n-l kód legyártása után adja. Ismét két problémát fogalmazhatunk meg.: (a) Soroljuk fel az összes k-bites Gray kódot (b) Írjuk fel ak-bites Gray kódok közül az n. et. Természetesen, a (b) probléma az általánosabb. De néha, ha csupán az (a) -problémát kell megoldanunk akkor ha az (a)-t megoldó algoritmusunk egyszerűbb és gyorsabb, mint az általánosabb (b)-t megoldó algoritmusunk, akkor érdemesebb azt használni (lásd például permutáció generálás esetét). Keressünk a fent már leírt módszemél egyszerűbb et az (a) problémához, valamint minél egyszerűbb megoldást a (b)-problémához! Jelöljük G(k) = (go. gp ...• g2k_) -val a k bites Gray kódokat. 23
  25. 25. Ekkor G-t a következő rekurzióvaldefmiálhatjuk: G(1) = (0,1){ G(n + 1) = (Ogo,Ogpo .. ,Og2n_lIg _, , ... , Ig ) (1) 2n oVagyis ezzel egyben módszert adtunk az (a) probléma megoldásához. Az (1) képletből könnyenmegkap hatjuk az m-edik Gray kód n. bitjét:g(m, n) = (mmOd2" 2n- <1 ) ( 2) Vagyis a (b) problémát akár egy, a (2) formulát használó algoritmussal is megoldhat juk,Legyen D(n) = (ct.,~,... ,d2n), úgy, hogy di gi hányadik biten tér el gi-l -től.akkor D(1) =1{ D(n+ 1) = (D(n),n+ (3) 1,D,ev(n)), ahol D,ev(n):= D(n) jordítottjaHaés n g; = (bn,bn_ p ••• ,1), hogy i = Llj2j (4) j=Oakkor b. =/. xor J- J J l, I j = 1,2, .... ,nEz utóbbi formula teremt kapcsolatot egy szám bináris alakja és a hozzá tartozó Gray kód között.Azezen a formulán alapuló algoritmusokat megvalósító program oknak megfelelő típusválasztás esetén(Pascal esetén a Word típus ilyen) nem kell tartalmazniuk bináris számmá alakító eljárást, hiszen azta számítógép változó-értékadáskor elvégzi. Sőt ha észreveszzük, hogya bj-t előállító sor egy binárisl-bittel jobbratolást fed, akkor a programunk a szám bináris alakjából egyetlen utasítássalelőállíthatja a megfelelő Gray kódot bitmintáját. Mivel a feladatok között találtam olyat, amelymegoldásához az (a) ill. (b) problémát is meg kell oldani, ezért a Gray kód generáló algoritmusunkatebbe ágyazva közöljük:USEC92-1A probléma neve Bit Twiddler:Származása: 1992 ACM East Central Regional Programming ContestA probléma lényege k-biten (k<=15 ) generálni a Gray kódokat az n.-től az m.-ig, n, m, k a programinputjai. A program outputja a megfelelő Gray kódok decimális alakja:A problémát kétrészproblémára lehet bontani: (1) k-biten az n-edik Gray kód generálása (2) valamely k-bites Gray kódból kiindulva a következő m darab k-bites Gray kód generálása Mivel a feladat k-ra vonatkozó korlátja (k<=15) elegendően kicsi, ezért használhat juk a már fent emIített pascalbeli Word típu st, valamint a memóriában elfér az összes l5-bites Gray-kód, amely 24
  26. 26. tartalmazza (l)-miatt az összes 14, 13, 12, ..,2, l-bites Gray kódot. Az init eljárás generálja a 15-bites Gray kódokat, ahogy ígértük egyetlen értékadással. A program többi eljárása akövetelményeknek megfelelő outputot (az adott Gray kód decimális alakját) állítja elő.program Twiddler;uses ert;type gray = array[O ..O] of word; {Csak a cím miatt}var g: Agray; b,j,t,i: word; proeedure init;{ A gray kódok előállítása} var i:word; begin for i:=O to maxint do gA[i] := (i xor (i shr 1)); end; proeedure put(tol,ig,biten:word); var i:word; begin for i:=tol to ig do writeln(gA[i]); end;be gin elrseri getmem(g,65535); init; repeat write(Mettől? );readln(t); write(Meddig? );readln(i); write(Hány biten? )ireadln(b); if b=O then halt(O); put(t,i,b-1) until falseend. Fig. 1.5.3.: Twiddler.pas 25
  27. 27. 2. Geometriai algoritmusokBevezetésA számítógépeket egyre többen és egyre gyakrabban használják olyan alapvetően geometriai eredetű,nagy mennyiségű adat feldolgozásával kapcsolatos problémák megoldására, mint az alakfelismerés, atérinformatikai, térképészeti vagy háromdimenziós szimulációs problémák. A geometriaialgoritmusok szintén nagyon fontosak az olyan komplex fizikai rendszerek tervezésében és aelemzésében, mint például az épületek, autók, gépek és integráltáramkörök. Az ilyenprogramrendszerekben a tervezők a fizikai objektumoknak megfeleitetett gépi objektumokkaldolgoznak. Ezen gépi objektumok megfelelő szintű számítógépes kezelése, megjelenítése igazánkomoly feladat.Az ilyen alkalmazások olyan alapvető objektumai, mint a pontok, szakaszok vagy poligonok és ahozzájuk kapcsolódó alapvető eljárások adják az általában a nehezebbek közé sorolható geometriaiversenyfeladatok megoldásának alapjait.A geometriai problémákat könnyen vizualizálhatók, de ez néha nem könnyíti meg a megoldáskeresését, inkább csak a probléma megértésében segítenek. Nagyon sok probléma megoldása, amely "szabadkézzel" pillanatok alatt megoldható egy darab papír és ceruza segítségével (pl. eldönteni, hogy egy pont egy poligon belsejében van -e vagy sem) követel egyáltalán nem triviális számítógépes programot.Néhány geometriai versenyfeladat megoldásához elegendő a középiskolai koordinátageometriából tanult alapvető függvények, eljárások (pl. pontok távolsága a síkon, egyenes és pont távolsága, háromszög területe) pontos (le)programozása. Ezek megoldása inkább a szimulációs problémák megoldásához hasonlít. További "geometriai" problémák megoldását tisztán vagy nagyrészt kombinatorikai algoritmusok adják, ezért az ilyen problémák elemzése előtt érdemes a "Kombinatorikai algoritmusok" című fejezetet (még egyszer) áttanulmányozni. A geometriai tartalmú versenyfeladatok harmadik csoportja olyan problémákból áll, amelyek megoldásához az első csoportbeli alapvető geometriai függvények pontos megvalósítása, valamint kombinatorikai alapismeretek szükségesek. A legtöbb algoritmus, amit tanulmányozni fogunk a legegyszerűbb geometriai objektumokkal fog dolgozni a két dimenziós véges, de megfelelően kiterjedt síkon. A legalapvetőbb geometriai objektumot a pontot egy számpárral fogjuk jellemezni (a pont koordinátái a derékszögú koordináta rendszerben). A szakasz reprezentációja egy pontpár, amelyeket az adott szakasz összeköt. A poligon esetünkben pontok listája, és feltételezzük, hogy az egymásután következő pontok szakaszokkal vannak összekötve, valamint, hogy az utolsó pontot az elsővel szintén egy szakasz köti össze, így alkotva egy zárt alakzatot. Az egységesség és az egyszerűség kedvéért rögzítsük le most, hogy hogyan fogjuk ezeket az objektumokat a programjainkban reprezentálni. A poligonok reprezentációja egy tömb. Használható lenne még láncolt lista is, de a listák alapvető műveleteinek programozása a pascalban egy kicsit sok adminisztrációt követel meg a programtói, ezért a tömbök használata a programokat egyszerűbbé teszi. Ha valamely probléma pontoknak egy halmazát érinti, akkor a reprezentációban szintén az array[O ..max] of pont definíciót fogjuk alkalmazni, ahol max valamilyen elegendően nagy szám.Tehát programjaink a következő reprezentációkat fogják használni: type pont = record x :integeri y: integer endi type szakasz = record pl:ponti p2:pont 26
  28. 28. endivar poligon: array{O ..max] of ponti Fig. 2. O.J.: Geometriai objektumok reprezentáció ja.Furcsának tűnhet, hogy a pontok a koordinátarendszer rácspontjaira korlátozódnak. A valósreprezentáció ugyanígy használható lenne, de az egész értékeket használata sokkal átláthatóbb éshatékonyabb (az egész számok műveleteit a számítógép sokkal gyorsabban végzi, mint alebegőpontosakat) programot eredményez, nem beszélve arról, hogy a versenyfeladatok általábanmegelégszenek az egész értékű geometriai programozással is. 27
  29. 29. 2.1. Szakaszok metszeteAz első probléma melyet tárgyalni fogunk az egyik legalapvetőbb probléma ezért számos másalgoritmus is hivatkozni fog rá, ezért nagyon fontos a precíz kidolgozása .. A probléma a következő:Adott két szakasz, amelyek ponttá is fajulhatnak (kezdő és végpontjuk egybeesik) eldöntendő, hogymetszik-e egymást vagy sem. A következő ábra néhány lehetséges előfordulást mutat: f· Fig. 2.1.1.: Szakaszok a síkon.A kézenfekvő algoritmus esetünkben az, hogy számítsuk ki annak a két egyenesnek a metszéspontját,amelyek tartalmazzák a kérdéses szakaszokat, majd vizsgáljuk meg, hogy a szakaszainktartalmazzák-e ezt a metszéspontot. Amennyiben a kérdéses egyenesek párhuzamosak, akkor azt kellvizsgálnunk, hogy valamely szakasz végpontja a másik szakaszra esik-e. Az egyenesekmetszéspont jának kiszámítása visszavezethető egy kétismeretlenes egyenletrendszer megoldására,amely útmutatást pl. [BELT1989]-ben találhatunk. function metszet (pO,pl,p2,p3: pont) :booleanj var nev,a,b,c,d,x,y:reali flagl,flag2,flag3,flag4:booleanj begin a:=pl.y-pO.Yi b:=pO.x-pl.x; c:=p3.y-p2.Yi d:=p2.x-p3.Xi nev:=a*d-b*ci {az egyenletrendszer matrixanak determinansa} if (abs(a)+abs(b»O) and (abs(c)+abs(d»O) {ha egyik szakasz sem pont} then if nev<>O {nem parhuzamosak} then begin { (x,y) a tartalmazo egyenesek metszespontja } y:=(a*(c*p2.x+d*p2.y)-c*(a*pO.x+b*pO.y))/nevi x:=(d*(a*pO.x+b*pO.y)-b*(c*p2.x+d*p2.y))/nevi flagl:=((pO.x<=x) and (x<=pl.x)) or ((pl.x<=x) and (x<=pO.x)); flag2:=((p2.x<=x) and (x<=p3.x)) or ((p3.x<=x) and (x<=p2.x))j flag3:=((pO.y<=y) and (y<=pl.y)) or ((pl.y<=y) and (y<=pO.Y))j flag4:=((p2.y<=y) and (y<=p3.y)) or ((p3.y<=y) and (y<=p2.Y))j metszet:=flagl and flag2 and flag3 and flag4 end else {a szakaszok parhuzamosak} 28
  30. 30. if a*p2.x+b*p2.y=a*pO.x+b*pO.y then begin flagl:=((( (pO.x<=p2.x) and (p2.x<=pl.x) ) or ( (pl.x<=p2.x) and (p2.x<=pO.x) ) )); flag2:=((( (pO.x<=p3.x) and (p3.x<=pl.x) ) or ( (pl.x<=p3.x) and (p3.x<=pO.x) ) )); flag3:=((( (p2.x<=pO.x) and (pO.x<=p3.x) ) or ( (p3.x<=pO.x) and (pO.x<=p2.x) ) )); flag4:=((( (p2.x<=pl.x) and (pl.x<=p3.x) ) or ( (p3.x<=pl.x) and (pl.x<=p3.x) ) )); metszet:= flagl or flag2 or flag3 or flag4; end else metszet:=false else {valamelyik szakasz pont l} if ((abs(a)+abs(b))>O) and (abs(c)+abs(d)=O) {a masodik szakasz pont} then if a*p2.x+b*p2.y=a*pO.x+b*pO.y then metszet:=((pO.x<=p2.x) and (p2.x<=pl.x)) or ((pl.x<=p2.x) and (p2.x<=pO.x)) else metszet:=false else if (abs(a)+abs(b)=O) and (abs(c)+abs(d»O) {az elso szakasz pont} then if c*pl.x+d*pl.y=c*p2.x+d*p2.y then metszet:=((p2.x<=pl.x) and (pl.x<=p3.x)) or ((p3.x<=pl.x) and (pl.x<=p2.x)) else metszet:=false else {mindketto pont} metszet:=(p2.x=p3.x) and (pO.y=p2.y) end; Fig. 2.1.2., Szakaszok metszetén ek vizsgálata.Sajnos a túl sok eset vizsgálata és kezelése eléggé bonyolult pascal kódot eredményez. Aversenyfeladatok megoldásához nem mindig szükséges a fentihez hasonló, általános algoritmus (pl.ha a szakaszok nem fajulnak pontokká, akkor az algoritmus és vele együtt a kód is lényegesenegyszerűbb). A fuggvény alkalmazhatóságát megnehezíti, hogy a kód nem eléggé átlátható és ezzelegyütt nehezen debug-olható,Az algoritmust megvalósító fuggvényt könnyedén át lehet alakítani úgy, hogy amennyiben vanmetszéspont annak koordinátáit adja is vissza, hiszen pl. nem elfajuló esetben a fuggvény x,yváltozójában a metszéspont koordinátái előállnak.A fentinél talán egyszerűbb és szintén általános algoritmust mutat be R. Sedgewick [SEDG1988]művében. A közölt algoritmus nem számítja ki explicit módon a szakaszokat tartalmazó egyenesekmetszéspontját, hanem a szakaszvégpontok elhelyezkedése alapján dönti el, hogy van-e metszéspont.Sedgewick közli az algoritmus pascal kódját is, de az sajnos nem működik. Viszont az algoritmusnakmegvan az az előnye a fentivel szemben, hogy a (helyes) pascal kódja talán kevesebb sorból állna. 29
  31. 31. Kapcsolódó problémák:P2.2.1. Adott n szakasz, eldöntendö, hogy zárt poligont alkotnak-e.P2.2.2. Adott n szakasz, eldöntendö, hogy páronként metszik-e egymást.P2.2.3. Hány metszéspont ja van egy adott n csúcsú konvex poligon átlóinak.Irodalomjegyzék a 2.1.1. fejezethez.[SEDG 1988]: Robert Sedgewick: Algorithms. Second Edition. 1988. p. 349.[BELTI989]: Bélteky Károly: Analitikus Geometria és Lineáris Algebra. 1989. 30
  32. 32. 2.2. Poligon és pontA versenyeken gyakran tűnnek fel olyan feladatok, melyek arra az alapproblémára vezethetők vissza,amelyben az algoritmusnak el kell döntenie, hogy egy adott pont egy adott (konvex vagy konkáv)poligon belsejében van-e. A kérdést emberi intelligenciával, szabadkézzel meglehetősen egyszerűeldönteni, viszont a gépi megoldás már nem triviális.Az egyik legegyszerűbb algoritmus a következő: l. Ellenőrizzük, hogy a vizsgálandó pont nem esik-e a poligon valamely csúcsára vagy oldalára. Ha igen akkor a pont az adott poligon belső pontja. 2. Keressünk egy olyan pontot a síkon, amely biztosan az adott poligonon kívül van. Mivel a poligont véges sok pont feszíti ki, ezért valamely irányban egy elegendően távoli pont biztosan a poligonon kívülre esik. Legyen ez a pont Q 3. A vizsgálandó pontból (P) húzzunk egy egyenest Q-ba. 4. Vizsgáljuk meg, hogy a PQ egyenesnek van-e közös pontja a poligon csúcsaival, ha igen keressünk egy a mostani Q-tól különböző új külső pontot-o 2. pont 5. Számláljuk meg a PQ egyenes és a poligon oldalainak metszéspontjainak számát. Ha ez páros akkor Papoligonon kívül volt, páratlan esetben P a poligon belső pontja.A 4.pontbeli feltétel az ábrán is látható kivételek miatt kell ellenőriznünk. A programunkban egyadott P-hez a megfelelő Q-t a következő módszerrel keressük meg: 4.1. Megkeressük azt a poligon[i] csúcsot, amely a poligon legjobboldalibb csúcsa 4.2.q.x:= poligon[i] .x+1, q.y:=poligon[i].y 4.3. Ellenőrizzük, hogy Q megfelel-e a fentieknek, ha nem q. y: =q. y+ 1, majd .....-;3.3.pont.Mivel a poligon véges sok csúcsból áll, ezért a 4.3 ciklus véges sokszor (legfeljebb n-szer, ha n acsúcsok száma) fog végrehajtódni, véges sok lépésben megtaláljuk a megfelelő Q pontot. Q Q p ------..---," Fig. 2.2. J.: Pont és Poligon elhelyezkedései a síkon A fenti algoritmus egy lehetséges megvalósítását mutatja a következő program: 31
  33. 33. program insideiuses crticonst max=20itype pont=record x,y:integer endivar poligon: array[O ..max] of ponti p,q:ponti count,i,n:integeri ctrl, ctr12:booleaniprocedure InputPoligonivar i:integeribegin clrscri write(n :)ireadln(n)i writeln(-------------------------------------------)i for i:=l to n do begin writeln(Az " i , -edik csucs koordinatai)i write(x: )ireadln(poligon[i] .X)i write(y: )ireadln(poligon[i] .y)i endi poligon[O] :=poligon[n]iendiprocedure InputPontibegin writeln(A pont koordinatai)i write(x: )ireadln(p.x)i write(y: )ireadln(p.y)iendibegin InputPoligoni InputPonti ctr12:=falsei i:=Oi repeat ctr12:=ctr12 or metszet2(p,p,poligon[i],poligon[i+l])i .i nc t í.j r until (ctr12) or (i=n-l)i if ctr12 then writeln(*8enne) else be gin q.x:=-maxinti for i:=l to n do begin if poligon[i] .x > q.x then begin q.x:=poligon[i] .x+li q.y:=poligon[i].y endi endi repeat q.y:=q.y+li ctrl:=falsei for i:=l to n do ctrl:=ctrl or metszet(poligon[i],poligon[i],p,q)i until not ctrli 32
  34. 34. if (p.x = q.x) and (p.y q.y) then writeln(*Kivul) else begin count:=O; for i:=O to n-l do if metszet(poligon[i], poligon[i+l], p,q) then inc(count); if odd(count) then writeln(*Benne ) else writeln(*Kivul ); end end; end. Fig. 2.2.2., A fenti program eldönti, hogy egy adott pont egy adott poligon belsejében van e.Az algoritmus egyetlen feltételezést támaszt az input adatokkal szemben, feltételezi, hogy azok amegadott sorrendben egy zárt, önmagát nem metsző alakzat valamilyen rögzített körbejárás szerinticsúcsai. Az alábbi poligonokat algoritmusunk inputjaként meg lehet úgy adni, hogy kielégítsék efeltételt, ezért ezek az esetek is kezelhetők:Sedgewick az előző problémánál hivatkozott könyvében erre a problémára is ad megoldást.Sedgewick algoritmusa csakúgy, mint a fenti, egy félegyenest húz a vizsgálandó pontból egy fiktív, apoligonon garantáltan kívülre eső ponton át, ám bármilyen legyen is ez (mindegy, hogy a poligoncsúcsain megy át vagy a poligon valamely oldalát tartalmazza ), ennek a félegyenesnek és apoligonnak számolja össze a metszéspontjait, úgy, hogy közben figyeli az előforduló speciáliseseteket, de sajnos a fenti ábrán láthatókhoz hasonló esetekkel nem tud mit kezdeni.Kapcsolódó problémák:P2.2.1. Írjunk hatékony algoritmust, amely eldönti, hogy egy pont egy konvex poligon belsejébenvan-elP2.2.2. Írjunk olyan pascal függvényt, amely eldönti, hogy két háromszögnek van-e metszéspont ja.Kapcsolódó versenyfeladatokFeladat InstrukcióRUGB92-3 Itt csak azt kell vizsgálni, hogy a sík egy adott pontja egy adott háromszög belsejébe esik-e, ezért a feladat jóval egyszerűbb módszerrel is megoldhatóIOAG91-2 33
  35. 35. 2.3. Ponthalmaz konvex burkaEbben a fejezetben egy olyan algoritmust kell készítenünk, amely meghatározza egy adottponthalmaz konvex burkát. A konvex burok nem más, mint egy konvex poligon, melynek csúcsai azadott ponthalmaz pontjai közül valók és a többi pont a poligon oldalaira vagy belsejébe esik. Fig. 2.3.1.: Ponthalmazok konvex hurka.A pontosság kedvéért egyértelművé kell még tennünk azt az esetek, amikor a ponthalmaz 3 vagy többpontja egy egyenesbe esik a ponthalmaz határán. Ilyenkor algoritmusunk outputját csak a poligoncsúcsaira eső pontok alkotják. Azaz a 2.3.1 b) ábrán látható ponthalmaz konvex burkát azABCDEFGH-val megjelölt pontokkal azonosítjuk.A konvex burok egyik tulajdonsága, hogy bármely a poligonon kivüli egyenest a poligon felémozgatva az a poligont egy csúcsában érinti. Ezt a tulajdonságot alkalmazva a koordinátatengelyekkel párhuzamos egyenesekre kapjuk, hogy a pontok közül azok, amelyek minimális ill.maximális x illetve y koordinátákkal rendelkeznek biztosan a burokhoz fognak tartozni. Ezt a ténythasználjuk fel a soron következő algoritmusunk kiindulásaként.Algoritmusunk bemenete pontok egy halmaza, amelyet egy array[J..max] alpont fog reprezentálni.A kimenete pedig egy poligon. Minthogy a poligon típus is egy előbbihez hasonló tömb típus, ezértaz algoritmus a bemenetként kapott tömb elemeit egyszeruen átrendezi, úgy, hogy az első Mtömbelem fogja a ponthalmaz konvex burkát reprezentálni.Látnunk kell azt, hogy azzal, hogy megtaláltuk és valamilyen módon megjelöltük a ponthalmazkonvex burkát alkotó pontokat, még nem oldottuk meg teljesen a problémát. Az így megjelöltpontokat ugyanis úgy kell sorrendbe rendeznünk, hogy azok valóban poligont alkossanak. Azaz a 2.3.1 b) ábrán látható ponthalmazt inputként megadva a GBCEDFAH output nem elfogadható.Tehát a csúcspontként megjelölt pontokat, még rendeznünk kell. Ha ezen pontok (x, y) derékszögűkoordinátáit (r, d) polárkoordinátákká alakítjuk (ahol d az Origótól mért távolság, r pedig a pont helyvektorának valamely rögzített egyenessei vett szöge), majd a pontokat a hozzájuk tartozó r érték alapján rendezzük akkor a kívánt sorrendet kapjuk. Megfontolandó az, hogy az egész probléma nem vezethető-e vissza a ponthalmaz pontjainak (r, d) szerinti lexikografikus rendezésére! Algoritmusunk azonban explicit módon semmilyen rendezést nem fog tartalmazni, lesz azonban egy kódrésze, amely a fent leírtak szerinti legkisebb r értékü pontot választja ki, anélkül, hogy trigonometrikus fuggvényeket használna. A trigonometrikus fuggvények kiküszöbölése az algoritmusból a megvalósított programot gyorsabbá, hatékonyabbá teszi. Az algoritmus: 1 Válasszuk ki a minimális y koordinátájú pontok közül azt amelynek a legkisebb x koordinátája van.. A fentiek alapján ez a pont biztosan a konvex burok csúcspontja lesz. Legyen ez a burok első pontja. 2 A burok második pontját úgy kaphat juk, hogy a burok első pontján át húzunk egy x- tengely jel párhuzamos egyenest (az előző pont garantálja, hogy ezen egyenes alatt, már nincsenek pontok), majd ezt az egyenest forgassuk az előző pont körül az óramutató 34
  36. 36. járásával ellentétes irányban addig, amíg valamely pontba ütközünk. Ha egyszerre több pontba ütköztünk akkor válasszuk ki a távolabbi pontot, és ez lesz a burok következő pontja. 3. A burok következő pontját úgy kapjuk, hogy egyenesünket az utoljára burokpontként megjelölt pont körül forgatjuk az eddigiekkel megegyező irányban addig amíg valamely pontba nem ütközünk. Ha egyszerre több pontot is elért az egyenesünk, akkor mindig a távolabbi pontot választ juk a burok következő pontjaként. 4. Ismételjük a 3.-beli eljárást addig míg a kiindulási pontunkat el nem értük. Fig. 2.3.2.: Ponthalmaz konvex burkát előállító algoritmus. Az algoritmusbeli feltételek garantálják, hogy a burokbeli pontokat a helyes sorrendben állítjuk elő.Az algoritmust megvalósító pascal program technikai megoldása az, hogy az így megjelölt pontokataz input tömb elejére cseréli és a már beválasztott pontokat nem vizsgálja újra. A következő ábra azalgoritmus működését szemlélteti: 1 1 4 3 3 1 1 4 4 5 5 3 3 1 1 Fig. 2.3.3.: A "csomagoló" eljárás működése. Visszatérve a konvex burok értelmezésére, megadására, az algoritmust és így a programot is egyszeruen át lehet alakítani, úgy, hogy az a burok csúcsoktói különböző pontjait is előállítsa 35
  37. 37. outputként. Egyszerűen akkor, amikor egyenesünk több ponttal ütközik egyszerre, akkor az érintettpontok közül a legközelebbit kell a burok következő csúcspontjaként megjelölnünk.Mindezek után az algoritmust megvalósító pascal program:program becsomagoliconst max=100itype pont=record x:integeri y:integer erid rvar inp,out:texti ok,s:stringi n,i,j,k:integeri poligon = array[O ..max] of pontiprocedure str_to_pont(s:stringivar q:pont)i var v,j,xl,x2:integeri sl:stringibegin v:=pos(,,s)i sl:=copy(s,l,v-l)i delete(s,l,v)i val(sl,xl,j)i val(s,x2,j)j q.x:=xli q.y:=x2ie nd rfunction theta(pl,p2:pont) :realivar dx,dy,ax,ay:integeri t:realibe gin dx:=p2.x-pl.xiax:=abs(dx)i dy:=p2.y-pl·Yiay:=abs(dY)i if (dx=O) and (dy=O) then t:=-l else t:=dy/(ax+aY)i if dx<O then t:=2-t else if dy<O then t:=4+t; theta:=t*90.00endifunction d(pl,p2:pont) :realivar dx,dy:realibegin dx:=p2.x-pl.Xi dy:=p2.y-pl·Yi d:=sqrt(dx*dx+dy*dy)endifunction csomag (c:integer) :integerivar i,min,m:integeri minszog,v,mintav,th:reali t:pontibegin min:=li 36
  38. 38. for i:=2 to n do if poligon[i] .y<poligon[min].y then min:=i else if poligon[i] .y=poligon [min] .y then if poligon[i] .x<poligon[min].x then min:=i; m:=O; poligon[n+l] :=poligon[min]; minszog:=-l; repeat inc (m) ; t:=poligon[m];poligon[m] :=poligon[min];poligon[min] :=t; min:=n+l; v:=minszog; minszog:=360.00imintav:=O; for i:=m+l to n+l do begin th:=theta(poligon[m],poligon[i])i if th >= v then if th < minszog then begin min:=ii minszog:=th;mintav:=d(poligon[m],poligon[min]) end else if th minszog then if c*d(poligon[m],poligon[i]) > c*mintav then begin min:=i; minszog:=thi mintav:=d(poligon[m),poligon[min)) end end until min=n+l; csomag:=m end;begin assign(inp, pontok.inp); assign(out, burok.out); reset(inp); rewrite(out); repeat readln(inp,s); n:=O; while s<>* do begin inc (n); str_to_pont(s,poligon[n))i readln(inp,s) end; for i:=l to csomag(-l) do writeln(out,poligon[i) .x,, ,poligon[i) .y) i writeln (out, *) until eof(inp)iclose(inp);close(out);end. 37
  39. 39. Fig. 2.3.4.: A Konvex Burok algoritmus.A program a pontok imp nevű imput állományból olvassa be a ponthalmazokat, és a burok.outnevű output állományba írja a megfelelő konvex burok csúcspontjait. Az állományok szerkezete akövetkező:Az input állomány blokkokból áll, egy blokk egy ponthalmaz pontjainak megadását tartalmazza,úgy, hogy egy sorban pontosan egy pont kordinátái találhatók vesszővel elválasztva. A blokk végét *jelzi.Az output állomány az inputként megadott ponthalmazok konvex burkát tartalmazza . Egy sorbanpontosan egy pont koordinátái vesszővel elválasztva. A burok végét egy új sorban egy * jelzi.A program csomag(c:integer) eljárása végzi a fent jellemzett eljárást. A c:integer paraméterértékétől függ, hogy az output állományba, melyik megadás szerint kerüljenek a poligon pontjai. c >O esetén, csak a poligon csúcspontjai, c < O esetén azon pontok is, amelyek a poligon oldalairaesnekA probléma általánosításának egyik lehetséges módja a több dimenziós térre történő kiterjesztése.Könnyű belátni, hogy a fenti algoritmus több dimenziós térben is helyesen működik, ha elkészítjük afenti pascal program több dimenziós, hipersíkokat is kezelő változatát.Kapcsolódó Problémák:P2.3.l. Írjunk olyan algoritmust, amely online módon adja meg az inputként megadott ponthalmazkonvex burkát. Az input pontokat egymás után egyesével adjuk meg az eljárásunknak és az eddigmegadott pontok konvex burkát adja meg lépésenként, módosítva azt minden újabb pont után haszükséges. lásd [SHAM1977]Kapcsolódó versenyfeladatok:Feladat InstrukcióIOAG91-2 A feladat megoldása mindkét eddig ismertetett algoritmus alkalmazását megköveteliACMF92-4 A konvex burok algoritmus explicit alkalmazása.KLTE93-2Irodalomjegyzék a 2.3 fejezethez [SHAM1977]: Shamos, M. 1. 1977, Computational geometry [GRAHI972]: Graham, R. L. An efficent algorithm for determining the convex hull ofa fmite planar set. Inform. Processing Letters 1 1972. P 132-133 [PREP 1977]: Prepatra, F. P., Hong S. J.: Convex hulls of fmite sets in two and three dimensions. Communications of ACM, Vol 20. Num 2., p 87-93 38
  40. 40. 3. GráfalgoritmusokBevezetésA programozói versenyek "nehezebb" feladatainak legnagyobb hányadát a gráfelméleti feladatokteszik ki. Ezek a feladatok lehetnek expliciten gráf elméleti fogalmakkal megfogalmazottak(olyanokkal, mint feszítőfa vagy legrövidebb út), vagy valamilyen a mindennapi életből vett köntösbebújtatottak.A gráf, mint matematikai objektum nem más, mint csúcsok és élek véges halmaza. A csúcsok alegkülönfélébb más objektumok lehetnek, egyetlen kikötésünk, hogy valamilyen megkülönböztetésüklétezzen: legyen nevük vagy más azonosítójuk. A gráf egy éle összeköttetést, kapcsolatot reprezentál a gráf pontosan két csúcsa között. ®A fenti ábrán látható ABCDF, G, EHI csúcspontokkal jellemzett rajzok alkothatnak egy kettő vagyhárom gráfot. Tekinthetjük például ABCDFG-t egy gráfnak. Ezt a gráfot definiálhat juk úgy, hogyfelsoroljuk a csúcsait: A, B, C, D, F, G majd az éleit AF, AD, BC, BD, Bf, CD.Egy X csúcspontból pontosan akkor vezet út egy Y csúcsba egy gráfban, ha létezik a csúcsoknak egyolyan listája, amelyben az egymás után következő csúcsok a gráfban éllel vannak összekötve. A gráfösszefüggő minden csúcsából létezik út minden más csúcsába. Pl a fenti ABCDF gráf összefüggő,míg az ABCDFEHI gráf nem összefüggő. A gráf egy egyszerű útja azon út amelyhez tartozólistában nincs ismétlődő csúcspont. A gráf egy kör-e azon egyszerű út, amelyben a kezdő és avégpont megegyezik (vagyis a listában egyetlen ismétlődés van). Azt a gráfot, amelyben egyetlen körsincs és összefüggő fá-nak nevezzük (a nem összefüggő, körmentes gráf ot erdő-nek nevezzük). Agráf feszítőfá-ja a gráf azon részgráfja, amely a gráf összes csúcsát tartalmazza, és pontosan annyiélt a gráf élei közül, hogy az így nyert részgráf összefüggő legyen. Könnyen látható, hogy n csúcsúgráf feszítőfájának pontosan n-l éle van. A feszítőfa másik ismert tulajdonsága, hogy bármely új élthozzáadva már kört kapunk. Azt a gráf ot, amely rendelkezik az összes lehetséges éllel teljes gráf-nak, amelyik viszonylag kevés éllel rendelkezik ritka gráf-nak, amelyik elég sok éllel rendelkezik sűrű gráf-nak nevezzük. A gráf ok gépi reprezentációiról nagyon hasznos dolgokat tudhatunk meg [SEDGI988]-ban valamint [NIE 11977] -ben. A gráf algoritmusok bonyolultsága (itt most bonyolultságon az adott körülmények közötti legrövidebb idejű megvalósíthatóságot kell érteni, ennek egyik leglényegesebb komponense a kód rövidsége és egyszerűsége) nagyban függ attól, hogy a gráfok milyen reprezentációját választottuk. Általában a legegyszerűbb kódot azon algoritmusok eredményezik, amelyekben a gráf reprezentációjaként valamilyen mátrixos (szomszédsági vagy összefüggőségi) megoldást választottunk. Persze vannak problémák, amelyeknél a listás reprezentáció elkerülése sokkal "költségesebb" lenne, mint annak megvalósítása. Irodalomjegyzék a 3. fejezethez [SEDGI988]: Robert Sedgewick: Algorithms pp418 - 421 [NIEI1977]: J. Nievergelt, J. C. Farrar, E. M. Reingold: Matematikai Problémák Megoldásainak Számítógépes Módszerei. 72-73 oldal 39
  41. 41. 3.1. Mélységi keresésAz egyik legklasszikusabb gráf elméleti algoritmus a keresés. Ebben a problémában olyan kérdésekrekell az algoritmusnak válaszolnia, mint van-e az adott gráfban kör, vagy melyek a gráf összefüggőkomponensei.Fogalmazzuk meg először, mit is kell most keresésen értenünk! Legyen adott a gráfnak két csúcsa,nevezetesen START és GOAL, a feladat megkeresni a STARTból a GOALba vezető utat, vagyis azta listát amelynek első elem START utolsó eleme pedig GOAL és az egymás után következőlistaelemeket a gráfban él köti össze. l. Meg kell vizsgálnunk, hogya START csúcs megegyezik-e a GOAL-al, ha igen, akkor kereső eljárásunk már véget is ért hiszen a keresett listának egyetlen elemből áll: a ST ART csúcsból. 2. Ha nem vagyunk ilyen "szerencsések", akkor keressük meg a START csúcs összes szomszédját, ezt a folyamatot kiterjesztésnek nevezzük. Az így nyert csúcsokat a START csúcs "gyermekeinek", "utódjainak" vagy "rákövetkezőinek" nevezzük. Ennek a lépésnek az eredménye egy lista. 3. Eztán a fenti két lépést alkalmazzuk az így nyert új csúcsokra, úgy, hogya 2. lépés után az újabb listát az előző listához fűzzük. p.z S1 csúcs kiterjesztése Fig. 3.1.1.: Altalános kereső eljárás működéseA módszerek különbözőségének okát abban kell keresnünk, hogy a 2. lépés után kapott csúcsokatmilyen sorrendben terjesztjük ki. A két legismertebb kereső stratégia a szélességi és a mélységikeresés. A mélységi keresés alkalmával mindig olyan "messze" haladunk a gráfban a STARTcsúcstól, amilyen messze csak lehet, vagyis a fenti ábrán mélységi stratégia szerint az S II csúcskövetkezne kiterjesztésre, szélességi stratégia szerint pedig az S2. De mindennél többet mond talán azalgoritmus formális leírása. INPUT: A gráf két csúcsa START és GOAL FELADAT: Meghatározni, hogy létezik-e egyszerű út START és GOAL között. OUTPUT: Igen vagy Nem 1. Legyen NYILTAK egy lista, első eleme legyen START Legyen ZÁRT szintén egy üres lista 2. Ha NYILTAK üres, akkor algoritmus vége és a válasz Nem 3. Vedd az első csúcsot a NYILTAK-ból legyen ez A. Szúrd A-t a ZÁRT lista elejére. a) Ha A = GOAL akkor az algoritmus vége és a válasz Igen. b) Hajtsd végre a kiterjesztést A-ra, ennek eredménye legyen az UTÓDOK listája. b1. Vedd ki az UTÓDOK listájából azon csúcsokat, melyek szerepelnek a ZÁRT listában b2. Fűzd az UTÓDOK listáját a NYILTAK listája elé. b3 Menj 2. -re Fig. 3.1. 2.: A mélységi keresés algoritmusa. 40
  42. 42. A fenti algoritmus, nem használja ki az élek reflexivitását, vagyis irányított gráf okra is tökéletesenműködik. A mélységi keresési stratégiát természetesen nem csak arra használhatjuk, hogymegkeressük két csúcs között egy gráfbeli utat. Használhatjuk pl. arra is, hogy megtudjuk, hogy egyadott gráf tartalmaz-e kört. Ugyanis ha a 3bl-ben ténylegesen ki kell vennünk valamely csúcsot aZÁRT listából, akkor ez azt jelenti, hogy ehhez a csúcshoz egy újabb útvonalon eljutottunk, vagyis,hogy a gráf kört tartalmaz. Azt is könnyen beláthatjuk, hogy a stratégia segítségével leválogathatjukegy gráf összefiiggő komponenseit: Meg kell jelölnünk a kiterjesztett csúcsokat és ha a NYILTAKlistája már üres és még van a gráfban meg nem jelölt csúcs, akkor ez azt jelenti, hogy ez a meg nemjelölt csúcs a gráf eddigitől különböző komponensében van. A témával kapcsolatban nincs már más hátra, mint a fenti ellenőrzéseket elvégző pascal program:program deptfirsti{ input formatum (a graf megadasa) 1. sor csucsok szama (v) 2. sor elek szama (e) 2+1. sor elso ,1: a ket csucs sorszama amelyeket osszekot [space]-szel elvalasztva. 2+2. sor masodik,l 2+e sor e-edik ,1const maxV=200;type link=Anode; node=record v :integer; next :link end;var inp,out:text; id,k,l,v,e,last:integer; components:integer; {ennyi osszefuggo komponens} graph: array[l ..MaxV] of link; order: array[l ..MaxV] of integer; cycle:boolean {=true ha van benne kor };procedure Str2Vertex(s:string;var k,j:integer);var v,h:integer; xl,x2:integer; sl:string; begin v:=pos( ,s); sl:=copy(s,l,v-l); delete(s,l,v); val(sl,k,h); val(s,j,h) end;procedure ReadInput;var j,i,x,y:integer; s:string; t:link;begin assign(inp, deptlst.inp); reset(inp); readln(inp,v); 41
  43. 43. readln(inp,e); for i:=l to v do graph[i] :=NIL; for j:=l to e do begin readln(inp,s);Str2Vertex(s,x,y); new(t);tA.v:=x; tA.next:=graph[y];graph[y] :=t; new(t);tA.v:=y; tA.next:=graph[x];graph[x] :=t; end;end;Procedure visit(k:integer); var t:link; begin inc(id);order[k] :=id; t:=graph[k]; while t<>NIL do begin if order[tA.v]=O then visit(tA.v) else cycle:=true; t:=tA.next end end; Procedure Conclusion; begin writeln(out, Osszefuggo komponensek: ,components); {/ A /} if cycle then writeln(kor :van ) else if components=l then writeln(fa) else writeln(erdo); writeln(out) end;begin assign(out, deptlst.out); rewrite(out); ReadInput; id:=O; last:=O; components:=O; for k:=l to v do order[k] :=0; for k:=l to v do begin last:=idi if order[k]=O then begin visit(k); inc(components); writeln(components, osszefuggo reszgraf:); for 1:=1 to v do if (order[l]<=id) and (order[l]>last) then write(l, I )i writeln; end end; Conclusion; close(out)end. Fig. 3.1.3.: A mélységi keresés.A program feltételezi, hogy az input fileban megadott csúcsok és élek egyetlen gráfhoz tartoznak. Aprogram az előzőek szerinti stratégiát használva alkalmas: -a gráf bejárására (minden csúcsot pontosan egyszer meglátogat) - a gráf összefüggő komponenseinek leválogatására 42

×