計算零到多個可能的數獨結果,並打印到Console中。
調用方法
MainController mc = new MainController(); mc.Do();
輸入
數獨數據 類型為int[,],-1表示空。
通過Sudu.Setup()方法設置。
輸出
同輸入格式。
通過Sudu.PrintResult()方法打印。
概述
該方法屬於窮舉的搜索算法。將完整過程分解為重復的往一個空里填一個數字的過程,每填一個空,嘗試所有可用數字,找到與已填數字不沖突的數字。如果找不到合適的數字,修改已填數字,既回溯到上一步。
代碼實現分兩部分,一個是填充循環,一個是填充位置改變和填充操作。分別實現為兩個類。
填充循環
思路
循環主體是執行操作(填寫一個數字)。
如果操作失敗(沒有有效的數字能夠填入),則移動到上一步。如果操作成功,則移動到下一步。
如果無法移動到下一步,則認為完成所有步驟,獲得了一個有效的結果。
如果無法移動到上一步,則認為無法修改步驟,結果查找完畢,退出執行循環。
代碼
public class MainController
{
public Sudu sudu = new Sudu();
/// <summary>
/// 執行循環
/// </summary>
/// <param name="findmulti">是否尋找多個解</param>
public void Do(bool findmulti = true)
{
sudu.PrintResult();
DateTime dt = DateTime.Now;
int resultcount = 0;
int actioncount = 0;
while (true)
{
// 執行
bool actionresult = sudu.TakeAction();
actioncount++;
if (actionresult)
{
// 執行成功則移動到下一步
if (sudu.MoveNext() == false)
{
// 無法移動到下一步,則已經完成
Console.WriteLine("嘗試次數:" + actioncount);
Console.WriteLine("結果個數:" + resultcount);
Console.WriteLine("耗時(s):" + (DateTime.Now - dt).TotalSeconds);
sudu.PrintResult();
resultcount++;
if (findmulti)
{
continue;
}
else
{
return;
}
}
}
else
{
// 執行失敗則退回上一步
if (sudu.MovePrev() == false)
{
// 無法移動到上一步,則已經結束搜索
Console.WriteLine("結束");
Console.WriteLine("嘗試次數:" + actioncount);
Console.WriteLine("結果個數:" + resultcount);
Console.WriteLine("耗時(s):" + (DateTime.Now - dt).TotalSeconds);
sudu.PrintResult();
return;
}
}
}
}
}
填充位置改變和填充操作
思路
數字填充:每次往數組中填充一個數字,如果能填充不沖突的數字則返回True,否則返回False。
填充位置改變:當前填充位置表示為行序號和列序號,初始狀態都為0。移動到下一步,上一步時,修改填充位置,即修改此序號對。
代碼
public class Sudu
{
public Sudu()
{
int[,] data = new int[,] {
{ -1,-1,2,9,-1,-1,-1,-1,-1},
{ -1,-1,-1,-1,-1,-1,8,-1,5},
{ -1,5,8,-1,-1,-1,7,-1,-1},
{ 1,-1,9,-1,3,-1,-1,-1,-1},
{ -1,-1,-1,-1,7,8,-1,-1,-1},
{-1,-1,6,-1,-1,-1,-1,3,-1 },
{9,4,-1,-1,5,-1,-1,-1,1 },
{-1,-1,-1,-1,-1,7,-1,-1,9 },
{6,8,-1,-1,-1,3,5,-1,-1 },
};
Setup(data);
}
/// <summary>
/// 初始化數獨數據。data中-1表示可填入,其他值表示固定值
/// </summary>
/// <param name="data"></param>
public void Setup(int[,] data)
{
int c = 0;
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (data[i, j] == -1)
{
filleddata[i, j] = 0;
canfill[i, j] = 1;
}
else
{
c = c + 1;
filleddata[i, j] = data[i, j];
canfill[i, j] = 0;
}
}
}
col = 0;
row = 0;
Console.WriteLine("已填數字:" + c);
}
/// <summary>
/// 初始值為0,表示未開始填充
/// </summary>
int[,] filleddata = new int[9, 9];
/// <summary>
/// 0代表不能填充,1代表能填充
/// </summary>
int[,] canfill = new int[9, 9];
int col = 0;
int row = 0;
/// <summary>
/// 移動到上一個可填空位置
/// </summary>
/// <returns>是否有上一個可填充位置</returns>
public bool MoveNext()
{
while (true)
{
if (col == 8 && row == 8)
return false;
if (col == 8)
{
row++;
col = 0;
}
else
{
col++;
}
if (canfill[row, col] == 0)
continue;
return true;
}
}
/// <summary>
/// 移動到下一個可填空位置
/// </summary>
/// <returns>是否有下一個可填充位置</returns>
public bool MovePrev()
{
while (true)
{
if (col == 0 && row == 0)
return false;
if (col == 0)
{
row--;
col = 8;
}
else
{
col--;
}
if (canfill[row, col] == 0)
continue;
return true;
}
}
/// <summary>
/// 填充當前位置
/// </summary>
/// <returns>如果正常填充則返回true,無法填充或填充無效則返回false</returns>
public bool TakeAction()
{
if (canfill[row, col] == 0)
return false;
//嘗試填充
while (true)
{
// 填充下一個值
if (filleddata[row, col] < 9)
filleddata[row, col] = filleddata[row, col] + 1;
else
{
// 重置該值
filleddata[row, col] = 0;
return false;
}
if (Check())
return true;
}
}
/// <summary>
/// 檢查沖突
/// </summary>
/// <returns></returns>
private bool Check()
{
for (int i = 0; i < 9; i++)
{
if (col != i
&& filleddata[row, col] == filleddata[row, i])
return false;
if (row != i
&& filleddata[row, col] == filleddata[i, col])
return false;
if (((row / 3 * 3 + i / 3) != row
&& (col / 3 * 3 + i % 3) != col)
&& filleddata[row / 3 * 3 + i / 3, col / 3 * 3 + i % 3] == filleddata[row, col])
return false;
}
return true;
}
/// <summary>
/// Console方式輸出數獨結果
/// </summary>
public void PrintResult()
{
Console.WriteLine("data");
Print(filleddata);
}
private void Print(int[,] data)
{
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
Console.Write(data[i, j] + ", ");
}
Console.WriteLine();
}
}
public void PrintCurrent()
{
Console.WriteLine("col: " + col + " row: " + row + " data: " + "data:" + filleddata[row, col]);
}
}
如需嘗試新的數獨,替換Sudu類構造函數中的數據即可。
have fun.
