用程序来解数独
數(shù)獨(dú)(Sudoku,記憶中這個(gè)游戲也叫九宮格,可能是我記錯(cuò)了,應(yīng)該叫數(shù)獨(dú)比較準(zhǔn)確)
數(shù)獨(dú)百度百科:http://baike.baidu.cn/view/961.htm
九宮格百度百科:http://baike.baidu.cn/view/230179.htm#sub5038177
?
前兩天看到 @萬(wàn)倉(cāng)一黍 關(guān)于數(shù)獨(dú)解法算法實(shí)踐的一篇文章(http://www.cnblogs.com/grenet/archive/2013/06/19/3138654.html),突然想起去年的時(shí)候,因?yàn)槲⒉┥弦黄P(guān)于某個(gè)外國(guó)學(xué)者花費(fèi)3個(gè)月,研究出一道只有一種答案的數(shù)獨(dú)題,然后自己一時(shí)熱血涌上,花了3個(gè)鐘寫了個(gè)程序解了這道題。
原微博地址:http://weibo.com/2305049812/z0k9MciY7?type=repost
我的程序結(jié)果:http://weibo.com/1974539725/z0mBU0AtX?type=repost
這道數(shù)獨(dú)題是這樣的:
?
接下來(lái)說(shuō)說(shuō)我當(dāng)時(shí)寫程序時(shí)的想法是怎樣的:
先拋開這道題,數(shù)獨(dú)的規(guī)則很簡(jiǎn)單,就是要每一行、每一列、每一個(gè)九宮格中都含有1-9九個(gè)數(shù)字,且不重復(fù)。那就可以根據(jù)這一規(guī)則來(lái)制定解題的流程、邏輯。
用程序來(lái)解題的好處就是,我們有“循環(huán)”、“遞歸”這兩個(gè)概念,而且計(jì)算機(jī)的計(jì)算速度遠(yuǎn)比人腦要快多了。
?
大概邏輯是:
1、先判斷每一格空格可能填寫的數(shù)字,取數(shù)量最少的那一格。
2、循環(huán)它可能的數(shù)字列,取第一個(gè)填入該格,再重復(fù)執(zhí)行該邏輯(遞歸調(diào)用)。
3、若得到的數(shù)字列為空,則跳回上一格,取下一個(gè)數(shù)字填入,繼續(xù)執(zhí)行查詢;若已跳回第一格,且已取完最后一個(gè)數(shù)字,則宣布該題無(wú)解;若已執(zhí)行到最后一個(gè),且得到數(shù)字列(理論上來(lái)將該數(shù)字列只有一個(gè)item),則宣布得到該題的解。
?
?
這里我使用Winform(C#)來(lái)做這個(gè)程序。
先設(shè)計(jì)好界面:
在后臺(tái)用一個(gè)數(shù)組變量保存這(9x9)81個(gè)格子
private static int _x = 9, _y = 9; private TextBox[,] _tbs = new TextBox[_x, _y];?
當(dāng)點(diǎn)擊“開始計(jì)算”按鈕后,后臺(tái)啟動(dòng)一個(gè)線程開始計(jì)算。
private bool Find() {// 循環(huán)每一格,找出各自可能情況數(shù)var count = 10;int[] indexs = null;IList<int> numbers = null;for (var i = 0; i < _x; ++i)for (var j = 0; j < _y; ++j){int val;if (string.IsNullOrEmpty(_tbs[i, j].Text.Trim()) || !int.TryParse(_tbs[i, j].Text.Trim(), out val)){var ns = GetNums(i, j);if (ns.Count == 0)return false;else if (ns.Count < count){count = ns.Count;indexs = new int[2] { i, j };numbers = ns;}}}// 選擇最少可能情況的那一格if (numbers == null)return true;else{var tb = _tbs[indexs[0], indexs[1]];do{tb.Text = numbers[0].ToString();if (Find())return true;elsenumbers.RemoveAt(0);}while (numbers.Count > 0);tb.Text = "";return false;} }// 獲取這一格所有可能的情況 private IList<int> GetNums(int x, int y) {// 獲取該格所在九宮格var cells = new List<TextBox>();int xstart = x / 3 * 3,ystart = y / 3 * 3;for (int i = xstart, iend = xstart + 3; i < iend; ++i)for (int j = ystart, yend = ystart + 3; j < yend; ++j)cells.Add(_tbs[i, j]);int val;var nums = new List<int>();for (var num = 1; num <= 9; ++num){if (nums.Contains(num)) continue;var isIn = false;// 橫行for (var index = 0; index < _y; ++index){if (index != y &&!string.IsNullOrEmpty(_tbs[x, index].Text.Trim()) &&int.TryParse(_tbs[x, index].Text.Trim(), out val) &&val == num){isIn = true;break;}}if (isIn) continue;// 豎行for (var index = 0; index < _x; ++index){if (index != x &&!string.IsNullOrEmpty(_tbs[index, y].Text.Trim()) &&int.TryParse(_tbs[index, y].Text.Trim(), out val) &&val == num){isIn = true;break;}}if (isIn) continue;// 九宮格var tb = _tbs[x, y];foreach (var c in cells){if (c != tb &&!string.IsNullOrEmpty(c.Text.Trim()) &&int.TryParse(c.Text.Trim(), out val) &&val == num){isIn = true;break;}}if (isIn) continue;// 可選數(shù)值 nums.Add(num);}return nums; }?
兩個(gè)大的方法已完成,接下來(lái)計(jì)算花費(fèi)的時(shí)間。以這道題為例,最后得出結(jié)果為:
???
?
這個(gè)可能不是最優(yōu)的邏輯算法,之前試過(guò)另一種方法,同樣可以得出這個(gè)結(jié)果,但花費(fèi)了70多秒,這個(gè)方法只花費(fèi)了19秒多。
貼出這篇文章只是為了學(xué)習(xí),希望各位前輩多多指教。
?
PS:貌似這道題的答案不止一種,之前做程序的時(shí)候至少得到兩種答案,不過(guò)結(jié)果沒(méi)保存下來(lái)。誰(shuí)知道呢,就讓其他人去研究吧 ...
轉(zhuǎn)載于:https://www.cnblogs.com/SugarLSG/p/3149113.html
總結(jié)
- 上一篇: Linux调优(文件系统)
- 下一篇: 【转】Snackbar和Toast的花式