日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++ - Sodoku Killer(DFS) - 实现一个数独解算器

發(fā)布時(shí)間:2023/12/14 c/c++ 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++ - Sodoku Killer(DFS) - 实现一个数独解算器 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 數(shù)獨(dú)相關(guān)知識(shí)
    • 什么是數(shù)獨(dú)
    • 解題技巧
  • 史上最難數(shù)獨(dú)
  • 普通解法
  • DFS解法
  • 編寫個(gè)數(shù)獨(dú)解釋器
  • 放上最難數(shù)獨(dú)來測試
  • 制作到游戲
  • Project
    • 編譯環(huán)境
  • References

以前挺喜歡玩數(shù)獨(dú)的。

覺得這些組合之間有這么“巧合”的關(guān)系,真的很神奇。

既然現(xiàn)在要重溫C++,那就用C++寫個(gè)數(shù)獨(dú)解算。

數(shù)獨(dú)相關(guān)知識(shí)

什么是數(shù)獨(dú)

什么是數(shù)獨(dú),詳細(xì)可參考1

一個(gè)好的數(shù)獨(dú)題,是只有一個(gè)解的,意思每個(gè)格子能填的數(shù)字最終是唯一的。

有一些沒有設(shè)計(jì)好的數(shù)獨(dú)題,會(huì)有多個(gè)解也稱無解

解題技巧

數(shù)獨(dú)技巧

我的數(shù)獨(dú)解算器就參考了里面的部分直觀法,與候選數(shù)法

史上最難數(shù)獨(dú)

在世界最難數(shù)獨(dú)2

普通解法

數(shù)獨(dú)技巧,之前說的這個(gè)都是普通解法。

DFS解法

除了普通解法,還有DFS數(shù)獨(dú)解法,這種解法一般是用于計(jì)算機(jī)(非人類)的計(jì)算方法。

DFS了解:

  • DFS數(shù)獨(dú)解法3
  • DFS(深度優(yōu)先搜索算法)4 - 這篇是說明與代碼都比較好
  • 深度優(yōu)先搜索5

因?yàn)镈FS需要滲透數(shù)獨(dú)的每個(gè)數(shù)字的分支結(jié)果。

試填數(shù)字的次數(shù)會(huì)非常非常大。

最糟糕時(shí)的時(shí)間復(fù)雜度為:O(!n)。

下面代碼我是參考了:3,我將原文的代碼中添加了更詳細(xì)的注釋:

這個(gè)版本的DFS算法,只要數(shù)獨(dú)的數(shù)字位置合理的,就可以解算出來。
如果設(shè)計(jì)數(shù)獨(dú)數(shù)字初始不足17個(gè),解算來了會(huì)有不定個(gè)解,也就是我們說的數(shù)獨(dú)無解,因?yàn)檫@并不數(shù)獨(dú)

// jave.lin - DFS解算數(shù)獨(dú) // 這個(gè)算法可以遞歸滲透9x9數(shù)獨(dú)的所有分支,前提是每個(gè)已填的數(shù)字是合理的 // 參考:https://blog.csdn.net/qq_42837885/article/details/90085051 #include<stdio.h> #include<stdlib.h>// #define SHOW_PROC // 是否顯示DFS處理過程int max_deep = 0; int rec_count = 0; // 檢測x,y格中,fillNum是否可填 bool check(int arr[9][9], int x, int y, int fillNum) {for (int i = 0; i < 9; i++) { // 檢測行、列if (arr[x][i] == fillNum || arr[i][y] == fillNum)return false; // 不能填}#ifdef READABLE // 可讀int maze[] = { 0,3,6 }; // 這個(gè)寫法很簡便,但是不利于閱讀int boxRow = maze[x / 3]; // 檢測宮格int boxCol = maze[y / 3];#else// 下面是比較高可讀的寫法:int boxRow = (x / 3) * 3; // 檢測宮格int boxCol = (y / 3) * 3;#endiffor (int i = boxRow; i < boxRow + 3; i++) {for (int j = boxCol; j < boxCol + 3; j++) {if (arr[i][j] == fillNum)return false; // 不能填}}return true; // 能填 } // 打印數(shù)獨(dú) void printSodoku(int arr[9][9]) {for (int i = 0; i < 9; i++) { //輸出for (int j = 0; j < 9; j++) {printf("%d", arr[i][j]);}printf("\n");}printf("max_deep:%d\n", max_deep);printf("rec_count:%d\n", rec_count); } // depth first search,深度搜索 void dfs(int arr[9][9], int x, int y, int deep, bool &ret) {// 統(tǒng)計(jì)最大遍歷深度max_deep = deep > max_deep ? deep : max_deep;// 統(tǒng)計(jì)遞歸次數(shù)++rec_count;// 當(dāng)遍歷當(dāng)?shù)?0行(即:索引為9的行)// 說明已經(jīng)遍歷完最后一行了if (x == 9) { //找到唯一最優(yōu)解// printf("x==9\n");printSodoku(arr);ret = true;return;}// 如果沒有填數(shù)字if (arr[x][y] == 0) {for (int i = 1; i <= 9; i++) { //遍歷合法數(shù)字填空if (check(arr, x, y, i) == 1) { //判斷是否合法arr[x][y] = i;//狀態(tài)轉(zhuǎn)移#ifdef SHOW_PROCprintSodoku();printf("fill:[%d][%d]=%d\n", x, y, i);system("pause");#endif// 下面的: next x = x + (y + 1) / 9; // y的逢9進(jìn)1作為x坐標(biāo)// next y = (y + 1) % 9; // wrap在9之內(nèi)作為y作為dfs(arr, x + (y + 1) / 9, (y + 1) % 9, deep + 1, ret); //遞歸下一個(gè)坐標(biāo)點(diǎn),即深搜}#ifdef SHOW_PROCelse {printf("can not fill:[%d][%d]=%d\n", x, y, i);}#endif}arr[x][y] = 0;//如果前面的遞歸都沒有找到唯一的最優(yōu)解,則回溯#ifdef SHOW_PROCprintf("reset:[%d][%d]=%d\n", x, y, 0);#endif}else {#ifdef SHOW_PROCprintf("[%d][%d]==%d, next to :[%d][%d]\n", x, y, arr[x][y], x + (y + 1) / 9, (y + 1) % 9);#endifdfs(arr, x + (y + 1) / 9, (y + 1) % 9, deep + 1, ret); //遞歸搜索下一個(gè)坐標(biāo)點(diǎn)} }int main() {// 目前世界最難的數(shù)獨(dú)題:// https://baike.baidu.com/item/%E4%B8%96%E7%95%8C%E6%9C%80%E9%9A%BE%E6%95%B0%E7%8B%ACint arr[9][9] = { {0,0,5,3,0,0,0,0,0},{8,0,0,0,0,0,0,2,0},{0,7,0,0,1,0,5,0,0},{4,0,0,0,0,5,3,0,0},{0,1,0,0,7,0,0,0,6},{0,0,3,2,0,0,0,8,0},{0,6,0,5,0,0,0,0,9},{0,0,4,0,0,0,0,3,0},{0,0,0,0,0,9,7,0,0} };// 測試無解題// int arr[9][9] = { { 0, 5, 2, 0, 0, 9, 0, 0, 0 },// { 0, 0, 3, 0, 5, 0, 0, 1, 6 },// { 0, 0, 4, 3, 0, 0, 0, 0, 5 },// { 0, 6, 0, 0, 0, 3, 8, 0, 0 },// { 0, 2, 0, 6, 9, 5, 0, 4, 0 },// { 0, 0, 4, 2, 0, 0, 5, 6, 0 },// { 0, 0, 7, 0, 0, 6, 3, 0, 0 },// { 2, 3, 0, 0, 7, 0, 6, 0, 0 },// { 0, 0, 0, 4, 3, 0, 0, 5, 0 },};// 傳入數(shù)獨(dú)數(shù)據(jù)// 從左上角0,0坐標(biāo)格子開始DFSprintSodoku(arr);printf(" === start dfs ===\n");bool ret = false;dfs(arr, 0, 0, 1, ret);if (ret) {printf("sodoku resolved!\n");} else {printf("sodoku invalidated!\n");}printf(" === end dfs ===\n");system("pause");return 0; }

輸出:

005300000 800000020 070010500 400005300 010070006 003200080 060500009 004000030 000009700 max_deep:0 rec_count:0=== start dfs === 145327698 839654127 672918543 496185372 218473956 753296481 367542819 984761235 521839764 max_deep:82 rec_count:14578 sodoku resolved!=== end dfs ===

編寫個(gè)數(shù)獨(dú)解釋器

數(shù)獨(dú)數(shù)據(jù):

// 骨灰級(jí)難度 int arr[9][9] = {// { 0, 0, 0, 0, 0, 0, 0, 0, 0 },// C1 C2 C3 C4 C5 C6 C7 C8 C9/*R1*/ { 0, 0, 0, 0, 5, 0, 0, 9, 0 },/*R2*/ { 0, 4, 2, 0, 0, 1, 7, 5, 0 },/*R3*/ { 0, 0, 0, 9, 0, 0, 6, 0, 3 },/*R4*/ { 0, 0, 7, 3, 0, 0, 0, 0, 4 },/*R5*/ { 0, 0, 9, 0, 0, 5, 0, 0, 0 },/*R6*/ { 0, 6, 0, 0, 2, 0, 0, 0, 0 },/*R7*/ { 0, 0, 0, 0, 0, 0, 2, 1, 9 },/*R8*/ { 0, 0, 0, 0, 0, 7, 0, 0, 0 },/*R9*/ { 0, 0, 8, 1, 0, 0, 3, 0, 0 },};

運(yùn)行效果:

6 7 3 2 5 8 4 9 1 9 4 2 6 3 1 7 5 8 8 1 5 9 7 4 6 2 3 5 2 7 3 1 6 9 8 4 3 8 9 7 4 5 1 6 2 1 6 4 8 2 9 5 3 7 7 5 6 4 8 3 2 1 9 2 3 1 5 9 7 8 4 6 4 9 8 1 6 2 3 7 5

放上最難數(shù)獨(dú)來測試

// (目前普通解題不了,只能DFS) 地獄級(jí)難度 - 世界最難數(shù)獨(dú):https://baike.baidu.com/item/%E4%B8%96%E7%95%8C%E6%9C%80%E9%9A%BE%E6%95%B0%E7%8B%AC// 注意:====>>>> 我這個(gè)數(shù)獨(dú)程序解算不了(其實(shí)我這個(gè)程序沒有回溯處理,意思,只要是需要試填的數(shù)獨(dú),我這個(gè)程序就可能解不了)// 只能使用DFS來解決// 這個(gè)數(shù)獨(dú)設(shè)計(jì)者是:芬蘭數(shù)學(xué)家因卡拉// 芬蘭數(shù)學(xué)家因卡拉,花費(fèi)3個(gè)月時(shí)間設(shè)計(jì)出了世界上迄今難度最大的數(shù)獨(dú)游戲,而且它只有一個(gè)答案。// 因卡拉說只有思考能力最快、頭腦最聰明的人才能破解這個(gè)游戲。這是英國《每日郵報(bào)》2012年6月30日的一篇報(bào)道。// 這個(gè)號(hào)稱“世界最難數(shù)獨(dú)”的“超級(jí)游戲”,卻被揚(yáng)州一位69歲的農(nóng)民花三天時(shí)間解了出來,但是將第四行第二列的5改成了8。// 而這個(gè)具有初中文化的老漢,數(shù)獨(dú)游戲啟蒙正是源于揚(yáng)子晚報(bào)。int arr[9][9] = {// { 0, 0, 0, 0, 0, 0, 0, 0, 0 },// C1 C2 C3 C4 C5 C6 C7 C8 C9/*R1*/ { 0, 0, 5, 3, 0, 0, 0, 0, 0 },/*R2*/ { 8, 0, 0, 0, 0, 0, 0, 2, 0 },/*R3*/ { 0, 7, 0, 0, 1, 0, 5, 0, 0 },/*R4*/ { 4, 0, 0, 0, 0, 5, 3, 0, 0 },/*R5*/ { 0, 1, 0, 0, 7, 0, 0, 0, 6 },/*R6*/ { 0, 0, 3, 2, 0, 0, 0, 8, 0 },/*R7*/ { 0, 6, 0, 5, 0, 0, 0, 0, 9 },/*R8*/ { 0, 0, 4, 0, 0, 0, 0, 3, 0 },/*R9*/ { 0, 0, 0, 0, 0, 9, 7, 0, 0 },};

運(yùn)行效果

145327698 839654127 672918543 496185372 218473956 753296481 367542819 984761235 521839764 max_deep:81 rec_count:14577 sodoku resolved!
  • max_deep 是DFS最大深度,剛好9*9=81,最大81次,剛好都遞歸過,這個(gè)數(shù)獨(dú)難度真的大。
  • rec_count 是遞歸的次數(shù),用了14577 次遞歸。

GIF運(yùn)行效果:

從上圖,可以看到,我們先使用了自己的數(shù)獨(dú)解算器,然后發(fā)現(xiàn)解算不了,就使用DFS來解算。

制作到游戲

以前我下載過手游數(shù)獨(dú),難度一般,最難的就專家級(jí)的。
以前玩的時(shí)候?qū)<壹?jí)大概就20~30分鐘即可解決。

有4宮,6宮,9宮格的

4宮格的比較簡單,基本都是7~9秒就可以解決。(適合小朋友入門)
6宮格的也是簡單。(適合學(xué)會(huì)入門后進(jìn)階)
9宮格玩多了都覺得差不多。因?yàn)檫@個(gè)手游最大難度就專家級(jí)了。

后來我去網(wǎng)上找了一些骨灰級(jí)、至尊級(jí),地獄級(jí)(就是我上面說的:史上最難數(shù)獨(dú),那題)。
真的很難,需要花很長時(shí)間,雖然可以算出來,但我就沒必要玩那些了,需要助記工具太多才能完成。

其實(shí)制作好了簡單的專家級(jí)9x9數(shù)獨(dú),基本就可以制作一款數(shù)獨(dú)游戲了,還可以提供解題提示,與記錄,方便回看解法。

然后在此基礎(chǔ)上還可以制作畸形數(shù)獨(dú)。
或是3D版數(shù)獨(dú)。

Project

backup :

  • Sodoku_vs_2019
  • Sodoku_gplusplus_2019

編譯環(huán)境

  • VS2019 C++ 項(xiàng)目 F5即可。
  • g++ 2019 項(xiàng)目
    • 編譯常數(shù):g++ .\Main.cpp .\PrintfCol.h .\Sodoku.cpp .\Sodoku.h .\Sodoku_Killer.cpp .\Sodoku_Killer.h -o Main
    • 運(yùn)行:.\Main.exe

References


  • 百科數(shù)獨(dú) ??

  • 世界最難數(shù)獨(dú) ??

  • 數(shù)獨(dú)游戲(dfs深搜) - 這個(gè)是比較簡潔的 ?? ??

  • DFS(深度優(yōu)先搜索算法) ??

  • 深度優(yōu)先搜索 ??

  • 總結(jié)

    以上是生活随笔為你收集整理的C++ - Sodoku Killer(DFS) - 实现一个数独解算器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。