Feladványok megoldása (1)

 

A Sudoku játék alapfeladata a feladványok megoldása. Két alapvetően különböző lehetőség van a megoldásra: kézi és gépi. Kézi megoldásnál nem használunk számítógépet a jelöltek meghatározására. Ekkor mi magunk fejtjük meg a rejtvényt, keresünk a feladványban olyan helyeket, ahova csak egyetlen szám írható. Ennek eredményessége természetesen személytől függő, de mindenképp időigényes mutatvány. Gépi megoldásnál a számítógép dolgozik helyettünk, különböző gépi algoritmusokat használva.

 

Jelölteknek nevezzük azokat az elemeket (számokat), amelyek egy bizonyos elemzés után a cellába beírhatóknak tűnnek. Cél az, hogy minél okosabb algoritmusokat használva a jelöltek számát a minimális 1-re csökkentsük. A jelöltek számának csökkentésére különböző metódusok állnak rendelkezésünkre, melynek alkalmazása révén elérhetjük a feladvány megfejtését. A metódusok közül néhány könnyen alkalmazható kézi megoldásnál is, és ráadásul olyanok is vannak közöttük, amelyek igen hatékonyak. Az is előfordulhat (a kézi és a gépi megoldásnál egyaránt), hogy nem létezik minden állapotban egyértelmű továbblépés (azaz nincs egyetlen olyan mező sem, ahol a jelöltek száma 1 lenne). Ekkor a jelöltek közül (ha van olyan cella, amelyben a jelöltek száma 2, akkor célszerű ebből) találomra kiválasztunk egyet, és ezzel addig próbálkozunk, ameddig teljesen kitöltjük a táblát, vagy valamely cellában a jelöltek száma 0 nem lesz (nem sikerül a kitöltés). Ha nem sikerül a kitöltés végére érni, akkor ki kell törölni a véletlenül kiválasztott elemet, (és minden olyat is törölni kell, amit ez után írtunk be!), és másik jelölttel kell próbálkoznunk. Ennek a gépi megfelelője a BackTrack algoritmus. Ha a metódusok közé ezt is felvesszük, akkor – legalább is géppel – minden megoldható feladványt meg is tudunk oldani.

 

Következzenek a metódusok. A szakirodalomban szokásos nevükkel fogom használni őket. Rögtön az első olyan – Basic – amelyet én neveztem el így, de szerintem elég logikusra sikeredett. A metódusok hatását egy (géppel segített) kézi megoldó program segítségével mutatom be, melynek az alapképernyője így néz ki:

 

 

         A feladványokat beírhatjuk kézzel (a bal oldali rácsba), vagy adatbázisból választhatjuk ki egy lenyíló ListBox segítségével. Válasszuk ki például a D-24.gsf adattábla 111-es feladványát:

 

 

A baloldali StringGrid-ben megjelent a feladvány, a jobboldaliban a jelöltek. Az adattáblákon a rekordléptető gombokkal mozoghatunk (Első, Előző, Következő, Utolsó és Ugrás – a beírt index alapján). Látható, hogy a feladvány 24 elemszámú, az alap metódus (Basic) szerint 3 rögtön beírható jelöltet tartalmaz (zöld háttérszín). A képernyő további részeinek leírása: A Timer vagyis időzítő, ami be van kapcsolva, azaz bármilyen változtatás a feladványban, vagy változtatás a metódusok ki-be kapcsolt állapotán, azonnal kiértékelődnek, azaz a jelöltek táblája aktualizálódik. Megnézhetjük a feladvány Alap tábláját (megoldását), Feladványt írhatunk be, Rögzíthetjük a gépi Megoldás számára, Újra kezdhetjük a beírást, és Fájlba menthetjük a kézzel beírt feladványt. Meg lehet vizsgálni, hogy a feladvány Megoldható-e? Látjuk a megoldhatósági vizsgálat kezdő időpontját és a végét, valamint az Igen (vagy Nem) választ. Külön láthatjuk az aktuális feladvány adatbázisbeli Indexét (rekordszámát) és a Nehézségi Fokát (minél nagyobb az érték, annál nehezebben oldható meg a feladvány). Az angol szín feliratú gombok (Red, Yellow, Olive) segítségével a jelölteket tartalmazó cellák háttérszínét állíthatjuk be (a jelen bemutatás könnyebb megértése végett), a Töröl gomb az alapértelmezett szint adja vissza. A Be és Ki gomb a metódusok csoportos ki-be kapcsolását végzik.

 

A lapon található Pascal rutinok a következő deklarációkat használják:         

 

Type
  KTomb=Array[1..Max] Of Word; //kis (9) tomb
  TTomb=Array[1..MX] Of Word;  //teljes (81 - nagy) tomb

Var SM: TTomb;                 //feladvany tombje
    SA: TTomb:                 //alaptabla tombje
    SC: TTomb;                 //ideiglenes feladvanytomb
    SH: Array[1..MX] Of KTomb//elemei 0 vagy 1 - a jelolteket tartalmazza
    SzTArray[1..MX,1..20] Of Word; //szobatarsak indexenek tombje

 

A továbbiakban nézzük az egyes metódusok bemutatását.

 

Basic (BS)

 

A szakirodalom nem nevesíti azt a metódust, amely a kitöltési szabályból közvetlenül származik. Én Basic-nek neveztem el. Ez az első lépés akkor, ha sikerül egy újabb elemet beírni a feladványba (egy cellában csak egy jelölt volt). Ilyenkor minden szobatársából a beírt számot törölni kell, amennyiben abban szerepelt. Most a kézi megoldóban egyetlen metódus sincs aktiválva csak a BS, amely ki sem kapcsolható. A lentebbi feladványban a C8-as cellába csak egyetlen jelölt található, ez a 2-es. Ha ezt beírjuk, akkor a C oszlopból, a 8-as sorból és az A7C9-es tartományból minden 2-es törlődik. (Az érintett cellákat piros háttérszín jelzi. A zöld háttérszín mindig a beírhatót – az egyetlen jelöltet jelöli.)

 

 

A C8-as cellába (sárga háttérszín a feladvány rácsban) történő beírás után a jelöltek így változnak meg:

 

 

         A Basic rutinja:

 

Procedure TSzal.Basic;
Var I, J: Word;
Begin
  For I:= 1 To MX Do If SM[I]>0 Then
  Begin
    //mezo teljes torlese
    For J:= 1 To Max Do SH[I,J]:= 0;
    //torles a szobatarsakban
    For J:= 1 To 20 Do SH[SzT[I,J],SM[I]]:= 0;
  End;
End;

 

Singles Out (SO)

 

Ez egy sajátos módszert, melyet az SO jelölőnégyzettel kapcsolhatunk ki-be. Ezt én Singles Outnak neveztem el. Lényege, hogy minden egyértelmű jelöltet beírtnak tekint, majd a BS-el a szoba többi cellájából törli a jelöltet. Majd újra kezdi, és mindaddig folytatja, amíg újabb beírhatót – azaz szinglit – talál. Nagyon jól látható hatása egy olyan feladvány megoldásánál, ahol a teljes feladvány csak Basic segítségével megoldható. Ilyen a D-20.gsf adattábla 2318-as feladványa.. Ebben a feladványban az elemek száma 20 és 1-es nehézségi fokú – ahogy látható. A feladványnak csak a B4-es cellájában van egyetlen jelölt.

 

 

Most kapcsoljuk be az SO funkciót, meglepő dolog fog történni. Minden be nem töltött cellában egyetlen jelölt lesz, gyakorlatilag az SO teljesen megoldotta a feladványt (Elemszám: 20, Beírható: 61). Íme:

 

 

A Singles Out rutinja:

 

Function Osszeg(A: KTomb): Word; //A[] tomb elemeinek osszege
Var I, S: Word;
Begin
  S:= 0; For I:= 1 To Max Do S:= S+A[I]; Osszeg:= S;
End;

Procedure SingleOut;
Var I, J, N: Word;
    Volt: Boolean;
Begin
  Volt:= True;
  While Volt Do
  Begin
    Volt:= False;
    For I:= 1 To MX Do If (SM[I]=0) And (Osszeg(SH[I])=1) Then
    Begin
      N:= 0; For J:= 1 To Max Do If SH[I,J]=1 Then Begin N:= J; Break End;
      For J:= 1 To 20 Do If SH[SzT[I,J],N]<>0 Then
      Begin SH[SzT[I,J],N]:= 0; Volt:= True End;
    End;
  End;
End;

 

Hidden Singles (HS)

 

         A metódus: Ha egy sorban, egy oszlopban vagy egy tartományban csak egyetlen cellában van egy adott jelölt, akkor a cellába ez a jelölt beírható, és a szoba további celláiból a jelölt törölhető. (Ezzel a metódussal egy jelölt beírhatóvá válik az érintett cellába.) A kézi megoldóban a BS mellett a HS is be van kapcsolva. A HS bemutatására nézzük a következő feladványt. Látható, hogy az 5-ös sorban csak a B oszlopban található 3-as. (További ilyen esetek a feladványban: a 3-as sorban csak az I oszlopban van 1-es, és az 1-es sorban csak a G oszlopban található 6-os.)

 

 

Ha HS-t bekapcsoljuk, akkor ezt láthatjuk (1 cella helyett 5 cellában lett egy jelölt, ezek a zöld háttérszínűek, a H8-ban eddig is egy jelölt volt – zöld háttérszínnel):

 

 

Ha e mellé az SO-t is bekapcsoljuk, akkor válik teljessé a kép (a piros hátterű mezők jelöltjeinek száma csökkent, ez összesen 19 mezőt érintett és ezek között létrejött egy beírható jelölt is a H9 cellában, melynek háttérszínét zöldről pirosra változtattam – a változást érzékeltetendő. Ebből is látszik, hogy a Hidden Singles egy igen hatékony metódus a jelöltek számának csökkentésére, ugyanakkor kézi megoldásban is aránylag könnyen használható):

 

 

A Hidden Singles rutinja:

 

Function OszlopN(S,X: Word): Word; //az SH X oszlopaban S szama
Var I, N: Word;
Begin
  N:= 0; For I:= 1 To Max Do N:= N+SH[TIndex[X,I],S]; OszlopN:= N;
End;

Function SorN(S,Y: Word): Word;    //az SH Y soraban S szama
Var I, N: Word;
Begin
  N:= 0; For I:= 1 To Max Do N:= N+SH[TIndex[I,Y],S]; SorN:= N;
End;

Function TartN(S,T: Word): Word;   //az SH T tartomanyaban S szama
Var I, N: Word;
Begin
  N:= 0; For I:= Max*(T-1)+1 To Max*T Do N:= N+SH[I,S]; TartN:= N;
End;

Procedure HiddenSingles;
Var I, J, L, M: Word;
    Volt: Boolean;
Begin

  //Hidden Singles, J: a keresendo szam
  Volt:= True;
  While Volt Do
  Begin
    Volt:= False;
    //ha egy oszlopban csak egy helyen van egy szammellole a tobbit torli
    For I:= 1 To Max Do For J:= 1 To Max Do If OszlopN(J,I)=1 Then
    Begin
      L:= 1; While SH[TIndex[I,L],J]=0 Do Inc(L);
      For M:= 1 To Max Do If (SH[TIndex[I,L],M]=1) And (M<>J) Then
      Begin SH[TIndex[I,L],M]:= 0; Volt:= True End;
    End;
    //ha egy sorban csak egy helyen van egy szammellole a tobbit torli
    For I:= 1 To Max Do For J:= 1 To Max Do If SorN(J,I)=1 Then
    Begin
      L:= 1; While SH[TIndex[L,I],J]=0 Do Inc(L);
      For M:= 1 To Max Do If (SH[TIndex[L,I],M]=1) And (M<>J) Then
      Begin SH[TIndex[L,I],M]:= 0; Volt:= True End;
    End;
    //ha egy tartományban csak egy helyen van egy szammellole a tobbit torli
    For I:= 1 To Max Do For J:= 1 To Max Do If TartN(J,I)=1 Then
    Begin
      L:= Max*(I-1)+1; While SH[L,J]=0 Do Inc(L);
      For M:= 1 To Max Do If (SH[L,M]=1) And (M<>J) Then
      Begin SH[L,M]:= 0; Volt:= True End;
    End;
  End;

End;

 

Pointing Pairs (PP)

 

         A metódus: Ha egy tartományban egy jelölt csak két helyen van, de egy sorban vagy egy oszlopban, akkor a jelölt – a kijelölt teljes sorban vagy oszlopban – a többi helyről törölhető. A következő feladványban három ilyen jelölt-pár is található: az A4 és a B4-ben a 8-as jelölt az A4C6 tartományban, a G6 és a H6-ban a 4-es jelölt a G4I6 tartományban és az F7 és F8-ban az 5-ös jelölt a D7F9 tartományban teljesíti a feltételeket. A PP metódus bekapcsolása előtti állapot így néz ki:

 

 

Ha bekapcsoljuk a PP-t, akkor a pirossal jelölt cellákból az érintett jelöltek törlődnek (D4-ből a 8-as, F4-ből az 5-ös, míg D6, E6 és F6-ból a 4-es):

 

 

A Pointing Pairs rutinja:

 

Procedure PointingPairs;
Var I, J, K, A, B, U, V: Word;
Begin
  //Pointing Pairs
  //ha egy tartomanyban egy szam csak ket helyen van, de egy sorban vagy egy
  //oszlopban, akkor a kijelolt sorban vagy oszlopban a tobbi helyrol a szam
  //torolheto
  For I:= 1 To MX Do If SM[I]=0 Then For J:= 1 To Max Do //J: keresendo szam
  If TartN(J,InT[I])=2 Then
  Begin
    A:= 0; B:= 0; U:= 0; V:= 0; //a tartományon beluli helyek
    For K:= TKInd[I] To TKInd[I]+Max-1 Do If SH[K,J]=1 Then If A=0 Then
    Begin U:= K; A:= InX[K] End Else Begin V:= K; B:= InX[K] End;
    If (A<>0) And (A=B) Then For K:= 1 To MX Do If SM[K]=0 Then //egy oszlop
    If (InX[K]=A) And (K<>U) And (K<>V) Then SH[K][J]:= 0;

    A:= 0; B:= 0; U:= 0; V:= 0; //a tartományon beluli helyek
    For K:= TKInd[I] To TKInd[I]+Max-1 Do If SH[K,J]=1 Then If A=0 Then
    Begin U:= K; A:= InY[K] End Else Begin V:= K; B:= InY[K] End;
    If (A<>0) And (A=B) Then For K:= 1 To MX Do If SM[K]=0 Then //egy sor
    If (InY[K]=A) And (K<>U) And (K<>V) Then SH[K][J]:= 0;
  End;
End;

 

Naked Pairs (NP)

 

A metódus: Ha egy szobának két cellájában csak ugyanaz a két jelölt van, akkor a szoba többi cellájából (a kijelölt sorból vagy oszlopból, és ha egy tartományban vannak, akkor tartományból is) ezek a jelöltek törölhetők. A következő feladványban két pár cella is teljesíti a feltételeket: B6 és B7, valamint D8 és F8.

 

 

Ha bekapcsoljuk az NP-t akkor a piros hátterű cellákból a megfelelő jelöltek (4-6 és 4-5) törlődnek (a B9-ben csak egy jelölt maradt, de a változás érzékeltetése miatt a hátterét pirosra állítottam). Mivel a 8-as sorban csak a D és F oszlopban található 4-es és 5-ös, ezért csak a D7F9 tartományban törlődnek jelöltek.

 

 

A következő feladványban a jelölt-pár egy tartományban van, de különböző sorokban és oszlopokban. Ekkor csak a tartományon belül történik jelölttörlés. Az A2 és C1 a megfelelő cellák és csak az A1C3 tartományban törlődik az 1-9 jelölt-pár.

 

 

Az NP bekapcsolása után ezt láthatjuk:

 

 

A Naked Pairs rutinja:

 

Function SHStr(K: KTomb): String;
Var I: Word;
    S: String;
Begin
  S:= ''; For I:= 1 To Max Do If K[I]=1 Then S:= S+IntToStr(I); SHStr:= S;
End;

Function Egyenlo(A, B: KTomb): Boolean//A és B egyenlo-e
Var I: Word;
Begin
  Egyenlo:= TrueFor I:= 1 To Max Do If A[I]<>B[I] Then
  Begin Egyenlo:= FalseBreak End;
End;

Function Kivesz(A, B: KTomb): KTomb//A-B: A-bol toroljuk B-t
Var I: Word;
    P: KTomb;
Begin
  P:= A; For I:= 1 To Max Do If B[I]=1 Then P[I]:= 0; Kivesz:= P;
End;

Procedure NakedPairs;
Var I, J, K: Word;
Begin
  //Naked Pairs
  //ha egy szobaban ket helyen van csak ugyanaz a ket szem,
  //akkor a tobbi helyrol ezek a szamok torolhetok
  For I:= 1 To MX-1 Do If SM[I]=0 Then If Length(SHStr(SH[I]))=2 Then
  For J:= I+1 To MX Do If SM[J]=0 Then If Length(SHStr(SH[J]))=2 Then
  Begin
    If (InX[I]=InX[J]) And Egyenlo(SH[I],SH[J]) Then //ha egy oszlopban vannak
    For K:= 1 To MX Do If SM[K]=0 Then
    If (InX[K]=InX[J]) And (K<>I) And (K<>J) Then
    Begin SH[K]:= Kivesz(SH[K],SH[J]) End;
    If (InY[I]=InY[J]) And Egyenlo(SH[I],SH[J]) Then //ha egy sorban vannnak
    For K:= 1 To MX Do If SM[K]=0 Then
    If (InY[K]=InY[J]) And (K<>I) And (K<>J) Then
    Begin SH[K]:= Kivesz(SH[K],SH[J]) End;
    If (InT[I]=InT[J]) And Egyenlo(SH[I],SH[J]) Then //ha egy tart.ban vannak
    For K:= 1 To MX Do If SM[K]=0 Then
    If (InT[K]=InT[J]) And (K<>I) And (K<>J) Then
    Begin SH[K]:= Kivesz(SH[K],SH[J]) End;
  End;
End;

 

Hidden Pairs (HP)

 

         A metódus: Ha egy szobában csak két cellában van ugyanaz a két jelölt (de más még lehet), akkor a szoba ezen két mezőjéből minden más jelölt törölhető. Nézzük a következő feladványt. A A oszlopban csak az 5-ös és a 9-es sorban van a 2-es és 3-as jelölt, a 7-es sorban csak az E és F oszlopban van 4-es és 7-es jelölt, valamint a G4I6 tartományban csak a G5-ös és I4-es cellákban van 4-es és 6-os jelölt. A HP bekapcsolása előtt a piros hátterű cellák jelzik a törlési helyeket:

 

 

Ha a HP-t bekapcsoljuk, akkor a törlések végrehajtódnak:

 

 

A Hidden Pairs rutinja:

 

Procedure HiddenPairs;
Var I, J, K, L, A, B, U, V: Word;
    OFiSFiTFiArray[1..Max] Of Boolean;
Begin
  //Hidden Pairs     - hasznaljuk az InX,InY és InT-t az egybetartozashoz
  For I:= 1 To Max Do Begin OFi[I]:= FalseSFi[I]:= FalseTFi[I]:= False End;
  //ha egy szobaban csak ket helyen van ugyanaz a ket szam (de mas meg lehet),
  //akkor a szoba ezen ket mezejebol minden mas szam torolheto
  For I:= 1 To MX Do If SM[I]=0 Then
  For J:= 1 To Max Do For K:= 1 To Max Do //J,K: a keresett szamok
  If J<>K Then
  Begin
    //ha mindkettobol ketto van egy oszlopban
    If Not OFi[InX[I]] Then
    If (OszlopN(J,InX[I])=2) And (OszlopN(K,InX[I])=2) Then
    Begin
      A:= 0; B:= 0; U:= 0; V:= 0;
      For L:= 1 To MX Do If SM[L]=0 Then
      If (InX[L]=InX[I]) And (SH[L,J]=1) Then
      If A=0 Then A:= L Else B:= L;
      For L:= 1 To MX Do If SM[L]=0 Then
      If (InX[L]=InX[I]) And (SH[L,K]=1) Then
      If U=0 Then U:= L Else V:= L;
      If (A=U) And (B=V) Then
      Begin
        For L:= 1 To Max Do If (L<>J) And (L<>K) Then SH[A,L]:= 0;
        For L:= 1 To Max Do If (L<>J) And (L<>K) Then SH[B,L]:= 0;
        OFi[InX[I]]:= True;
      End;
    End;
    //ha mindkettobol ketto van egy sorban
    If Not SFi[InY[I]] Then
    If (SorN(J,InY[I])=2) And (SorN(K,InY[I])=2) Then
    Begin
      A:= 0; B:= 0; U:= 0; V:= 0;
      For L:= 1 To MX Do If SM[L]=0 Then
      If (InY[L]=InY[I]) And (SH[L,J]=1) Then
      If A=0 Then A:= L Else B:= L;
      For L:= 1 To MX Do If SM[L]=0 Then
      If (InY[L]=InY[I]) And (SH[L,K]=1) Then
      If U=0 Then U:= L Else V:= L;
      If (A=U) And (B=V) Then
      Begin
        For L:= 1 To Max Do If (L<>J) And (L<>K) Then SH[A,L]:= 0;
        For L:= 1 To Max Do If (L<>J) And (L<>K) Then SH[B,L]:= 0;
        SFi[InY[I]]:= True;
      End;
    End;
    //ha mindkettobol ketto van egy tartomanyban
    If Not TFi[InT[I]] Then
    If (TartN(J,InT[I])=2) And (TartN(K,InT[I])=2) Then
    Begin
      A:= 0; B:= 0; U:= 0; V:= 0;
      For L:= 1 To MX Do If SM[L]=0 Then
      If (InT[L]=InT[I]) And (SH[L,J]=1) Then
      If A=0 Then A:= L Else B:= L;
      For L:= 1 To MX Do If SM[L]=0 Then
      If (InT[L]=InT[I]) And (SH[L,K]=1) Then
      If U=0 Then U:= L Else V:= L;
      If (A=U) And (B=V) Then
      Begin
        For L:= 1 To Max Do If (L<>J) And (L<>K) Then SH[A,L]:= 0;
        For L:= 1 To Max Do If (L<>J) And (L<>K) Then SH[B,L]:= 0;
        TFi[InT[I]]:= True;
      End;
    End;
  End;
End;