Sudoku tábla előállítása
ütközésmentesítő algoritmussal
A véletlen Latin négyzet előállításának negyedik
verziójában alkalmazott ütközésmentesítő algoritmust alkalmazzuk Sudoku táblák
előállítására.
A tapasztalat azt mutatja, hogy az ütközésmentesítéshez
a legtöbb esetben nem elegendő két elemet kiválasztani. Ha viszont hármas
elemcserét alkalmazunk, akkor olyan algoritmust kapunk, mely egyrészt nagyon
gyors (a futási idő soha nem volt nagyobb, mint két másodperc) és mindig
végeredményt adott, vagyis még nem sikerült egyetlen olyan esetet sem találni,
amikor nem tudta volna befejezni a tábla generálását. Mivel a fejlesztést a
kételemes cserékkel kezdtem, ennek a kipróbálása is lehetőség van a Rendez-2
nyomógomb segítségével. A tuti végeredményt természetesen a Rendez-3 nyomógomb
szolgáltatja.
A program generálás előtti futási képe (fehér és cián
alapszín az ütközésmentes, zöld alapszín az ütközéses mezőket jelenti):
Valamint
a hármas cserét alkalmazó generálás után:
A
program listája:
unit USudoUtk;
interface
uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls, Grids;
Const GAl=3;
Max=GAl*GAl;
type
TfmSudoUtk = class(TForm)
lbSudoUtk: TLabel;
btKilepes: TButton;
sgTabla: TStringGrid;
btInit: TButton;
lbMaxMenet: TLabel;
edMaxMenet: TEdit;
btRendez3: TButton;
edMenet: TEdit;
edUtOSz: TEdit;
edStart: TEdit;
edStop: TEdit;
lbKesz: TLabel;
btRendez2: TButton;
Procedure Init;
Procedure Tablara;
Function UTSz(O, S: Word): Word;
Procedure Utkozesek;
procedure btKilepesClick(Sender: TObject);
procedure sgTablaDrawCell(Sender: TObject; Col, Row: Integer;
Rect: TRect; State: TGridDrawState);
procedure FormCreate(Sender: TObject);
procedure btInitClick(Sender: TObject);
procedure edMaxMenetChange(Sender: TObject);
procedure btRendez3Click(Sender: TObject);
procedure btRendez2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
fmSudoUtk: TfmSudoUtk;
SUD, UTT: Array[1..Max,1..Max] Of Word;
MaxMenet, Menet, UTOsz: LongInt;
implementation
{$R *.dfm}
procedure TfmSudoUtk.btKilepesClick(Sender: TObject);
begin
Close;
end;
procedure TfmSudoUtk.sgTablaDrawCell(Sender: TObject; Col, Row: Integer;
Rect: TRect; State: TGridDrawState);
begin
With sgTabla.Canvas.Brush Do
Begin
If Not((gdSelected In State) Or (gdFixed In State)) Then
If Odd(((Col-1) Div GAl)+((Row-1) Div GAl)) Then
Color:= clAqua Else Color:= clWindow;
If UTT[Col,Row]>0 Then Color:= clLime;
End;
sgTabla.Canvas.TextRect(Rect,Rect.Left+11,Rect.Top+2,
sgTabla.Cells[Col,Row]);
If gdFocused In State Then sgTabla.Canvas.DrawFocusRect(Rect);
end;
procedure TfmSudoUtk.edMaxMenetChange(Sender: TObject);
Var Kod: Integer;
begin
Val(edMaxMenet.Text,MaxMenet,Kod);
end;
procedure TfmSudoUtk.FormCreate(Sender: TObject);
begin
Randomize;
With sgTabla Do
Begin
ColCount:= Max+2;
RowCount:= Max+2;
ColWidths[0]:= 0;
RowHeights[0]:= 0;
ColWidths[ColCount-1]:= 0;
RowHeights[RowCount-1]:= 0;
Col:= ColCount-1;
Row:= RowCount-1;
End;
Init;
Tablara;
MaxMenet:= 100000;
end;
Procedure TfmSudoUtk.Init;
Var I, J, A, B, P: Word;
Begin
For I:= 1 To Max Do For J:= 1 To Max Do SUD[I,J]:= I;
For I:= 1 To Max Do For J:= 1 To 10000 Do
Begin
A:= Random(Max)+1; B:= Random(Max)+1;
P:= SUD[A,I]; SUD[A,I]:= SUD[B,I]; SUD[B,I]:= P;
End;
Menet:= 0;
Utkozesek;
edMenet.Text:= IntToStr(Menet);
edUtOSz.Text:= IntToStr(UTOSz);
End;
Procedure TfmSudoUtk.Tablara;
Var I, J: Word;
Begin
With sgTabla Do
Begin
For I:= 1 To Max Do For J:= 1 To Max Do Cells[I,J]:= IntToStr(SUD[I,J]);
RePaint;
End;
End;
procedure TfmSudoUtk.btInitClick(Sender: TObject);
begin
Init; Tablara;
end;
Function TfmSudoUtk.UTSz(O, S: Word): Word;
Var I, N, P, Q: Word;
Begin
N:= 0;
For I:= 1 To Max Do If (I<>O) And (SUD[I,S]=SUD[O,S]) Then Inc(N);
For I:= 1 To Max Do If (I<>S) And (SUD[O,I]=SUD[O,S]) Then Inc(N);
For P:= O-((O-1) Mod GAl) To O-((O-1) Mod GAl)+GAl-1 Do
For Q:= S-((S-1) Mod GAl) To S-((S-1) Mod GAl)+GAl-1 Do
If Not ((P=O) And (Q=S)) And (SUD[P,Q]=SUD[O,S]) Then Inc(N);
UTSz:= N;
End;
Procedure TfmSudoUtk.Utkozesek;
Var I, J: Word;
Begin
UTOSz:= 0; For I:= 1 To Max Do For J:= 1 To Max Do
Begin UTT[I,J]:= UTSz(I,J); Inc(UTOSz,UTT[I,J]) End;
End;
procedure TfmSudoUtk.btRendez2Click(Sender: TObject);
Var X1, Y1, X2, Y2, P, A, B: Word;
begin
edStart.Text:= TimeToStr(GetTime); edStart.Repaint;
edStop.Text:= ''; edStop.Repaint;
lbKesz.Caption:= ' '; lbKesz.Repaint;
Menet:= 0;
While (UTOSz>0) And (Menet<MaxMenet) Do
Begin
Inc(Menet); Utkozesek; A:= UTOSz;
X1:= 1; Y1:= 1;
While UTT[X1,Y1]=0 Do Begin X1:= Random(Max)+1; Y1:= Random(Max)+1 End;
X2:= Random(Max)+1; Y2:= Random(Max)+1;
P:= SUD[X1,Y1]; SUD[X1,Y1]:= SUD[X2,Y2]; SUD[X2,Y2]:= P;
Utkozesek; B:= UTOSz;
If B>A Then
Begin P:= SUD[X2,Y2]; SUD[X2,Y2]:= SUD[X1,Y1]; SUD[X1,Y1]:= P End;
If Menet Mod 10000=0 Then
Begin
edMenet.Text:= IntToStr(Menet); edMenet.RePaint; Tablara;
edUtOSz.Text:= IntToStr(UTOSz); edUTOsz.RePaint;
End;
End;
Tablara;
edMenet.Text:= IntToStr(Menet);
edUtOSz.Text:= IntToStr(UTOSz);
With lbKesz Do If UTOSz=0 Then
Caption:= 'Kész' Else Caption:= 'Vége';
lbKesz.Visible:= True;
edStop.Text:= TimeToStr(GetTime);
end;
procedure TfmSudoUtk.btRendez3Click(Sender: TObject);
Var X1, Y1, X2, Y2, X3, Y3, P, A, B: Word;
begin
edStart.Text:= TimeToStr(GetTime); edStart.Repaint;
edStop.Text:= ''; edStop.Repaint;
lbKesz.Caption:= ' '; lbKesz.Repaint;
Menet:= 0;
While (UTOSz>0) And (Menet<MaxMenet) Do
Begin
Inc(Menet); Utkozesek; A:= UTOSz; X1:= 1; Y1:= 1;
While UTT[X1,Y1]=0 Do Begin X1:= Random(Max)+1; Y1:= Random(Max)+1 End;
X2:= Random(Max)+1; Y2:= Random(Max)+1;
X3:= Random(Max)+1; Y3:= Random(Max)+1;
P:= SUD[X1,Y1]; SUD[X1,Y1]:= SUD[X2,Y2];
SUD[X2,Y2]:= SUD[X3,Y3]; SUD[X3,Y3]:= P;
Utkozesek; B:= UTOSz;
If B>A Then
Begin
P:= SUD[X3,Y3]; SUD[X3,Y3]:= SUD[X2,Y2];
SUD[X2,Y2]:= SUD[X1,Y1]; SUD[X1,Y1]:= P;
End;
If Menet Mod 10000=0 Then
Begin
edMenet.Text:= IntToStr(Menet); edMenet.RePaint; Tablara;
edUtOSz.Text:= IntToStr(UTOSz); edUTOsz.RePaint;
End;
End;
Tablara;
edMenet.Text:= IntToStr(Menet);
edUtOSz.Text:= IntToStr(UTOSz);
With lbKesz Do If UTOSz=0 Then
Caption:= 'Kész' Else Caption:= 'Vége';
lbKesz.Visible:= True;
edStop.Text:= TimeToStr(GetTime);
end;
end.