csaki

mestint gyak 1.

Feb 12th, 2014
197
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 17.45 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6.  
  7. namespace mestintgyak_1
  8. {
  9.  
  10.     class Program
  11.     {
  12.         static void Main(string[] args)
  13.         {
  14.             Csúcs startCsúcs;
  15.             GráfKereső kereső;
  16.             Console.WriteLine("A 3 szerzetes 3 kanibál problémát oldjuk meg.");
  17.             startCsúcs = new Csúcs(new SzerzetesekÉsKannibálokÁllapot(3, 3));
  18.             Console.WriteLine("A kereső egy 15 mélységi korlátos és emlékezetes backtrack.");
  19.             kereső = new BackTrack(startCsúcs, 15, true);
  20.             kereső.megoldásKiírása(kereső.Keresés());
  21.             Console.ReadLine();
  22.         }
  23.     }
  24.  
  25.  
  26.     /// <summary>
  27.     /// Minden állapot osztály őse.
  28.     /// </summary>
  29.     abstract class AbsztraktÁllapot : ICloneable
  30.     {
  31.         // Megvizsgálja, hogy a belső állapot állapot-e.
  32.         // Ha igen, akkor igazat ad vissza, egyébként hamisat.
  33.         public abstract bool ÁllapotE();
  34.  
  35.         // Megvizsgálja, hogy a belső állapot célállapot-e.
  36.         // Ha igen, akkor igazat ad vissza, egyébként hamisat.
  37.         public abstract bool CélÁllapotE();
  38.  
  39.         // Visszaadja az alapoperátorok számát.
  40.         public abstract int OperátorokSzáma();
  41.  
  42.         // A szuper operátoron keresztül lehet elérni az összes operátort.
  43.         // Igazat ad vissza, ha az i.dik alap operátor alkalmazható a belső állapotra.
  44.         // for ciklusból kell hívni 0-tól kezdve az alap operátorok számig. Pl. így:
  45.         // for (int i = 0; i < állapot.GetNumberOfOps(); i++)
  46.         // {
  47.         // AbsztraktÁllapot klón=(AbsztraktÁllapot)állapot.Clone();
  48.         // if (klón.SzuperOperátor(i))
  49.         // {
  50.         // Console.WriteLine("Az {0} állapotra az {1}.dik " +
  51.         // "operátor alkalmazható", állapot, i);
  52.         // }
  53.         // }
  54.         public abstract bool SzuperOperátor(int i);
  55.  
  56.         // Klónoz. Azért van rá szükség, mert némelyik operátor hatását vissza kell vonnunk.
  57.         // A legegyszerűbb, hogy az állapotot leklónozom. Arra hívom az operátort.
  58.         // Ha gond van, akkor visszatérek az eredeti állapothoz.
  59.         // Ha nincs gond, akkor a klón lesz az állapot, amiből folytatom a keresést.
  60.         // Ez sekély klónozást alkalmaz. Ha elég a sekély klónozás, akkor nem kell felülírni a gyermek osztályban.
  61.         // Ha mély klónozás kell, akkor mindenképp felülírandó.
  62.         public virtual object Clone() { return MemberwiseClone(); }
  63.  
  64.         // Csak akkor kell felülírni, ha emlékezetes backtracket akarunk használni, vagy mélységi keresést.
  65.         // Egyébként maradhat ez az alap implementáció.
  66.         // Programozás technikailag ez egy kampó metódus, amit az OCP megszegése nélkül írhatok felül.
  67.         public override bool Equals(Object a) { return false; }
  68.  
  69.         // Ha két példány egyenlő, akkor a hasítókódjuk is egyenlő.
  70.         // Ezért, ha valaki felülírja az Equals metódust, ezt is illik felülírni.
  71.         public override int GetHashCode() { return base.GetHashCode(); }
  72.     }
  73.  
  74.  
  75.     /// <summary>
  76.     /// A "3 szerzetes és 3 kannibál" probléma állapottér reprezentációja.
  77.     /// Illetve általánosítása akárhány szerzetesre és kannibálra.
  78.     /// Probléma: 3 szerzet és 3 kannibál van a folyó bal partján.
  79.     /// Át kell juttatni az összes embert a másik partra.
  80.     /// Ehhez rendelkezésre áll egy két személyes csónak.
  81.     /// Egy ember is elég az átjutáshoz, de kettőnél több ember nem fér el.
  82.     /// Ha valaki átmegy a másik oldalra, akkor ki is kell szállni, nem maradhat a csónakban.
  83.     /// A gond ott van, hogy ha valamelyik parton több kannibál van,
  84.     /// mint szerzetes, akkor a kannibálok megeszik a szerzeteseket.
  85.     /// Kezdő állapot:
  86.     /// 3 szerzetes a bal oldalon.
  87.     /// 3 kannibál a bal oldalon.
  88.     /// A csónak a bal parton van.
  89.     /// 0 szerzetes a jobb oldalon.
  90.     /// 0 kannibál a jobb oldalon.
  91.     /// Ezt az állapotot ezzel a rendezett 5-össel írjuk le:
  92.     /// (3,3,'B',0,0)
  93.     /// A célállapot:
  94.     /// (0,0,'J',3,3)
  95.     /// Operátor:
  96.     /// Op(int sz, int k):
  97.     /// sz darab szerzetes átmegy a másik oldalra és
  98.     /// k darab kannibál átmegy a másik oldalra.
  99.     /// Lehetséges paraméterezése:
  100.     /// Op(1,0): 1 szerzetes átmegy a másik oldalra.
  101.     /// Op(2,0): 2 szerzetes átmegy a másik oldalra.
  102.     /// Op(1,1): 1 szerzetes és 1 kannibál átmegy a másik oldalra.
  103.     /// Op(0,1): 1 kannibál átmegy a másik oldalra.
  104.     /// Op(0,2): 2 kanibál átmegy a másik oldalra.
  105.     /// </summary>
  106.     class SzerzetesekÉsKannibálokÁllapot : AbsztraktÁllapot
  107.     {
  108.         int sz; // ennyi szerzetes van összesen
  109.         int k; // ennyi kannibál van összesen
  110.         int szb; // szerzetesek száma a bal oldalon
  111.         int kb; // kannibálok száma a bal oldalon
  112.         char cs; // Hol a csónak? Értéke vagy 'B' vagy 'J'.
  113.         int szj; // szerzetesek száma a jobb oldalon
  114.         int kj; // kannibálok száma a jobb oldalon
  115.         public SzerzetesekÉsKannibálokÁllapot(int sz, int k) // beállítja a kezdő állapotot
  116.         {
  117.             this.sz = sz;
  118.             this.k = k;
  119.             szb = sz;
  120.             kb = k;
  121.             cs = 'B';
  122.             szj = 0;
  123.             kj = 0;
  124.         }
  125.         public override bool ÁllapotE()
  126.         { // igaz, ha a szerzetesek biztonságban vannak
  127.             return ((szb >= kb) || (szb == 0)) &&
  128.             ((szj >= kj) || (szj == 0));
  129.         }
  130.         public override bool CélÁllapotE()
  131.         {
  132.             // igaz, ha mindenki átért a jobb oldalra
  133.             return szj == sz && kj == k;
  134.         }
  135.         private bool preOp(int sz, int k)
  136.         {
  137.             // A csónak 2 személyes, legalább egy ember kell az evezéshez.
  138.             // Ezt végül is felesleges vizsgálni, mert a szuper operátor csak ennek megfelelően hívja.
  139.             if (sz + k > 2 || sz + k < 0 || sz < 0 || k < 0) return false;
  140.             // Csak akkor lehet átvinni sz szerzetest és
  141.             // k kannibált, ha a csónak oldalán van is legalább ennyi.
  142.             if (cs == 'B')
  143.                 return szb >= sz && kb >= k;
  144.             else
  145.                 return szj >= sz && kj >= k;
  146.         }
  147.         // Átvisz a másik oldalra sz darab szerzetes és k darab kannibált.
  148.         private bool op(int sz, int k)
  149.         {
  150.             if (!preOp(sz, k)) return false;
  151.             SzerzetesekÉsKannibálokÁllapot mentes = (SzerzetesekÉsKannibálokÁllapot)Clone();
  152.             if (cs == 'B')
  153.             {
  154.                 szb -= sz;
  155.                 kb -= k;
  156.                 cs = 'J';
  157.                 szj += sz;
  158.                 kj += k;
  159.             }
  160.             else
  161.             {
  162.                 szb += sz;
  163.                 kb += k;
  164.                 cs = 'B';
  165.                 szj -= sz;
  166.                 kj -= k;
  167.             }
  168.             if (ÁllapotE()) return true;
  169.             szb = mentes.szb;
  170.             kb = mentes.kb;
  171.             cs = mentes.cs;
  172.             szj = mentes.szj;
  173.             kj = mentes.kj;
  174.             return false;
  175.         }
  176.         public override int OperátorokSzáma() { return 5; }
  177.         public override bool SzuperOperátor(int i)
  178.         {
  179.             switch (i)
  180.             {
  181.                 case 0: return op(0, 1);
  182.                 case 1: return op(0, 2);
  183.                 case 2: return op(1, 1);
  184.                 case 3: return op(1, 0);
  185.                 case 4: return op(2, 0);
  186.                 default: return false;
  187.             }
  188.         }
  189.         public override string ToString()
  190.         {
  191.             return szb + "," + kb + "," + cs + "," + szj + "," + kj;
  192.         }
  193.         public override bool Equals(Object a)
  194.         {
  195.             SzerzetesekÉsKannibálokÁllapot aa = (SzerzetesekÉsKannibálokÁllapot)a;
  196.             // szj és kj számítható, ezért nem kell vizsgálni
  197.             return aa.szb == szb && aa.kb == kb && aa.cs == cs;
  198.         }
  199.         // Ha két példány egyenlő, akkor a hasítókódjuk is egyenlő.
  200.         public override int GetHashCode()
  201.         {
  202.             return szb.GetHashCode() + kb.GetHashCode() + cs.GetHashCode();
  203.         }
  204.     }
  205.  
  206.  
  207.     /// <summary>
  208.     /// A csúcs tartalmaz egy állapotot, a csúcs mélységét, és a csúcs szülőjét.
  209.     /// Így egy csúcs egy egész utat reprezentál a start csúcsig.
  210.     /// </summary>
  211.     class Csúcs
  212.     {
  213.         // A csúcs tartalmaz egy állapotot, a mélységét és a szülőjét
  214.         AbsztraktÁllapot állapot;
  215.         int mélység;
  216.         Csúcs szülő; // A szülőkön felfelé haladva a start csúcsig jutok.
  217.         // Konstruktor:
  218.         // A belső állapotot beállítja a start csúcsra.
  219.         // A hívó felelőssége, hogy a kezdő állapottal hívja meg.
  220.         // A start csúcs mélysége 0, szülője nincs.
  221.         public Csúcs(AbsztraktÁllapot kezdőÁllapot)
  222.         {
  223.             állapot = kezdőÁllapot;
  224.             mélység = 0;
  225.             szülő = null;
  226.         }
  227.         // Egy új gyermek csúcsot készít.
  228.         // Erre még meg kell hívni egy alkalmazható operátor is, csak azután lesz kész.
  229.         public Csúcs(Csúcs szülő)
  230.         {
  231.             állapot = (AbsztraktÁllapot)szülő.állapot.Clone();
  232.             mélység = szülő.mélység + 1;
  233.             this.szülő = szülő;
  234.         }
  235.         public Csúcs GetSzülő() { return szülő; }
  236.         public int GetMélység() { return mélység; }
  237.         public bool TerminálisCsúcsE() { return állapot.CélÁllapotE(); }
  238.         public int OperátorokSzáma() { return állapot.OperátorokSzáma(); }
  239.         public bool SzuperOperátor(int i) { return állapot.SzuperOperátor(i); }
  240.         public override bool Equals(Object obj)
  241.         {
  242.             Csúcs cs = (Csúcs)obj;
  243.             return állapot.Equals(cs.állapot);
  244.         }
  245.         public override int GetHashCode() { return állapot.GetHashCode(); }
  246.         public override String ToString() { return állapot.ToString(); }
  247.         // Alkalmazza az összes alkalmazható operátort.
  248.         // Visszaadja az így előálló új csúcsokat.
  249.         public List<Csúcs> Kiterjesztes()
  250.         {
  251.             List<Csúcs> újCsúcsok = new List<Csúcs>();
  252.             for (int i = 0; i < OperátorokSzáma(); i++)
  253.             {
  254.                 // Új gyermek csúcsot készítek.
  255.                 Csúcs újCsúcs = new Csúcs(this);
  256.                 // Kiprobálom az i.dik alapoperátort. Alkalmazható?
  257.                 if (újCsúcs.SzuperOperátor(i))
  258.                 {
  259.                     // Ha igen, hozzáadom az újakhoz.
  260.                     újCsúcsok.Add(újCsúcs);
  261.                 }
  262.             }
  263.             return újCsúcsok;
  264.         }
  265.     }
  266.  
  267.  
  268.     /// <summary>
  269.     /// Minden gráfkereső algoritmus őse.
  270.     /// A gráfkeresőknek csak a Keresés metódust kell megvalósítaniuk.
  271.     /// Ez visszaad egy terminális csúcsot, ha talált megoldást, egyébként null értékkel tér vissza.
  272.     /// A terminális csúcsból a szülő referenciákon felfelé haladva áll elő a megoldás.
  273.     /// </summary>
  274.     abstract class GráfKereső
  275.     {
  276.         private Csúcs startCsúcs; // A start csúcs csúcs.
  277.         // Minden gráfkereső a start csúcsból kezd el keresni.
  278.         public GráfKereső(Csúcs startCsúcs)
  279.         {
  280.             this.startCsúcs = startCsúcs;
  281.         }
  282.         // Jobb, ha a start csúcs privát, de a gyermek osztályok lekérhetik.
  283.         protected Csúcs GetStartCsúcs() { return startCsúcs; }
  284.         /// Ha van megoldás, azaz van olyan út az állapottér gráfban,
  285.         /// ami a start csúcsból egy terminális csúcsba vezet,
  286.         /// akkor visszaad egy megoldást, egyébként null.
  287.         /// A megoldást egy terminális csúcsként adja vissza.
  288.         /// Ezen csúcs szülő referenciáin felfelé haladva adódik a megoldás fordított sorrendben.
  289.         public abstract Csúcs Keresés();
  290.         /// <summary>
  291.         /// Kiíratja a megoldást egy terminális csúcs alapján.
  292.         /// Feltételezi, hogy a terminális csúcs szülő referenciáján felfelé haladva eljutunk a start csúcshoz.
  293.         /// A csúcsok sorrendjét megfordítja, hogy helyesen tudja kiírni a megoldást.
  294.         /// Ha a csúcs null, akkor kiírja, hogy nincs megoldás.
  295.         /// </summary>
  296.         /// <param name="egyTerminálisCsúcs">
  297.         /// A megoldást képviselő terminális csúcs vagy null.
  298.         /// </param>
  299.         public void megoldásKiírása(Csúcs egyTerminálisCsúcs)
  300.         {
  301.             if (egyTerminálisCsúcs == null)
  302.             {
  303.                 Console.WriteLine("Nincs megoldás");
  304.                 return;
  305.             }
  306.             // Meg kell fordítani a csúcsok sorrendjét.
  307.             Stack<Csúcs> megoldás = new Stack<Csúcs>();
  308.             Csúcs aktCsúcs = egyTerminálisCsúcs;
  309.             while (aktCsúcs != null)
  310.             {
  311.                 megoldás.Push(aktCsúcs);
  312.                 aktCsúcs = aktCsúcs.GetSzülő();
  313.             }
  314.             // Megfordítottuk, lehet kiírni.
  315.             foreach (Csúcs akt in megoldás) Console.WriteLine(akt);
  316.         }
  317.     }
  318.  
  319.  
  320.  
  321.     /// <summary>
  322.     /// A backtrack gráfkereső algoritmust megvalósító osztály.
  323.     /// A három alap backtrack algoritmust egyben tartalmazza. Ezek
  324.     /// - az alap backtrack
  325.     /// - mélységi korlátos backtrack
  326.     /// - emlékezetes backtrack
  327.     /// Az ág-korlátos backtrack nincs megvalósítva.
  328.     /// </summary>
  329.     class BackTrack : GráfKereső
  330.     {
  331.         int korlát; // Ha nem nulla, akkor mélységi korlátos kereső.
  332.         bool emlékezetes; // Ha igaz, emlékezetes kereső.
  333.         public BackTrack(Csúcs startCsúcs, int korlát, bool emlékezetes)
  334.             : base(startCsúcs)
  335.         {
  336.             this.korlát = korlát;
  337.             this.emlékezetes = emlékezetes;
  338.         }
  339.         // nincs mélységi korlát, se emlékezet
  340.         public BackTrack(Csúcs startCsúcs) : this(startCsúcs, 0, false) { }
  341.         // mélységi korlátos kereső
  342.         public BackTrack(Csúcs startCsúcs, int korlát) : this(startCsúcs, korlát, false) { }
  343.         // emlékezetes kereső
  344.         public BackTrack(Csúcs startCsúcs, bool emlékezetes) : this(startCsúcs, 0, emlékezetes) { }
  345.         // A keresés a start csúcsból indul.
  346.         // Egy terminális csúcsot ad vissza. A start csúcsból el lehet jutni ebbe a terminális csúcsba.
  347.         // Ha nincs ilyen, akkor null értéket ad vissza.
  348.         public override Csúcs Keresés() { return Keresés(GetStartCsúcs()); }
  349.         // A kereső algoritmus rekurzív megvalósítása.
  350.         // Mivel rekurzív, ezért a visszalépésnek a "return null" felel meg.
  351.         private Csúcs Keresés(Csúcs aktCsúcs)
  352.         {
  353.             int mélység = aktCsúcs.GetMélység();
  354.             // mélységi korlát vizsgálata
  355.             if (korlát > 0 && mélység >= korlát) return null;
  356.             // emlékezet használata kör kiszűréséhez
  357.             Csúcs aktSzülő = null;
  358.             if (emlékezetes) aktSzülő = aktCsúcs.GetSzülő();
  359.             while (aktSzülő != null)
  360.             {
  361.                 // Ellenőrzöm, hogy jártam-e ebben az állapotban. Ha igen, akkor visszalépés.
  362.                 if (aktCsúcs.Equals(aktSzülő)) return null;
  363.                 // Visszafelé haladás a szülői láncon.
  364.                 aktSzülő = aktSzülő.GetSzülő();
  365.             }
  366.             if (aktCsúcs.TerminálisCsúcsE())
  367.             {
  368.                 // Megvan a megoldás, vissza kell adni a terminális csúcsot.
  369.                 return aktCsúcs;
  370.             }
  371.             // Itt hívogatom az alapoperátorokat a szuper operátoron
  372.             // keresztül. Ha valamelyik alkalmazható, akkor új csúcsot
  373.             // készítek, és meghívom önmagamat rekurzívan.
  374.             for (int i = 0; i < aktCsúcs.OperátorokSzáma(); i++)
  375.             {
  376.                 // Elkészítem az új gyermek csúcsot.
  377.                 // Ez csak akkor lesz kész, ha alkalmazok rá egy alkalmazható operátort is.
  378.                 Csúcs újCsúcs = new Csúcs(aktCsúcs);
  379.                 // Kipróbálom az i.dik alapoperátort. Alkalmazható?
  380.                 if (újCsúcs.SzuperOperátor(i))
  381.                 {
  382.                     // Ha igen, rekurzívan meghívni önmagam az új csúcsra.
  383.                     // Ha nem null értéket ad vissza, akkor megvan a megoldás.
  384.                     // Ha null értéket, akkor ki kell próbálni a következő alapoperátort.
  385.                     Csúcs terminális = Keresés(újCsúcs);
  386.                     if (terminális != null)
  387.                     {
  388.                         // Visszaadom a megoldást képviselő terminális csúcsot.
  389.                         return terminális;
  390.                     }
  391.                     // Az else ágon kellene visszavonni az operátort.
  392.                     // Erre akkor van szükség, ha az új gyermeket létrehozásában nem lenne klónozást.
  393.                     // Mivel klónoztam, ezért ez a rész üres.
  394.                 }
  395.             }
  396.             // Ha kipróbáltam az összes operátort és egyik se vezetett megoldásra, akkor visszalépés.
  397.             // A visszalépés hatására eggyel feljebb a következő alapoperátor kerül sorra.
  398.             return null;
  399.         }
  400.     }
  401. }
Advertisement
Add Comment
Please, Sign In to add comment