go 实现暴力破解数独

2024-01-28 10:04
文章标签 实现 go 暴力破解 数独

本文主要是介绍go 实现暴力破解数独,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一切罪恶的来源是昨晚睡前玩了一把数独,找虐的选了个最难的模式,做了一个多小时才做完,然后就睡不着了..........程序员不能受这委屈,今天咋样也得把这玩意儿破解了

破解思路(暴力破解加深度遍历)

  1. 把数独看做是一个二维数组,并且这个数组已经填了一部分数字,空格填数字0
  2. 依次遍历这个数组,找到第一个空格,他所能填的数字为同一行,同一列以及所在小九宫格均未出现的数字,所能填的数字可能有多个,依次进行尝试(正确的那个数字肯定能走到最后,错误的数字在后面一定会发生矛盾,矛盾就是有一个空格1-9都不能填)
  3. 填完一个空格之后,就形成了新的数独,递归重复这个操作即可

代码实现

package mainimport ("fmt""os"
)
func shuzu_print(shuzu [9][9]int) {fmt.Println("------------------------")for i := 0; i < 9; i++ {fmt.Println(shuzu[i])}
}func main() {shuzu := [9][9]int{ // 初始化数独{2, 0, 0, 0, 4, 0, 0, 1, 0},{6, 1, 7, 0, 0, 3, 0, 0, 5},{0, 5, 0, 0, 0, 0, 0, 0, 3},{0, 0, 9, 0, 3, 6, 5, 7, 8},{0, 6, 5, 0, 8, 0, 0, 4, 2},{0, 0, 8, 4, 1, 5, 0, 6, 9},{0, 0, 0, 3, 0, 0, 0, 9, 0},{0, 9, 2, 0, 7, 0, 8, 3, 0},{8, 3, 0, 9, 2, 0, 0, 5, 7},}shuzu_print(shuzu)// 打印handle(shuzu)}
func handle(shuzu [9][9]int) {for i := 0; i < 9; i++ {for j := 0; j < 9; j++ {if i == 8 && j == 8 { // 已经填完了,这个exit 是为了打断所有尝试shuzu_print(shuzu)os.Exit(1)return}if shuzu[i][j] != 0 {continue}set := make(map[int]struct{}) // map 实现set,存储同行,同列,所在小九宫格已经存在的数字for k := 0; k < 9; k++ {if shuzu[i][k] != 0 {set[shuzu[i][k]] = struct{}{}}}for k := 0; k < 9; k++ {if shuzu[k][j] != 0 {set[shuzu[k][j]] = struct{}{}}}i_3 := i / 3j_3 := j / 3for ii := i_3 * 3; ii < (i_3+1)*3; ii++ {for jj := j_3 * 3; jj < (j_3+1)*3; jj++ {if shuzu[ii][jj] != 0 {set[shuzu[ii][jj]] = struct{}{}}}}if len(set) == 9 { // 如果同行,同列,所在小九宫格1-9均存在了,那么发生矛盾,此支线走不下去return}for kk := 1; kk < 10; kk++ {if _, ok := set[kk]; !ok {shuzu[i][j] = kkhandle(shuzu)// 同行,同列,所在小九宫格1-9不存在的数字均要进行尝试}}}}}

 跑了一下没问题,而且是秒出结果,但是很悲剧,下面这个例子(就是我昨晚那个关卡的例子)就不行了,二十分钟都跑不完,调试了一下,0行1列的位置可以填3,8,9(正解是8),但是拿3去尝试的时候,一直到第二行填完才出现矛盾,并且这个过程大概花了五六秒的时间,也太暴力了吧

	shuzu := [9][9]int{{1, 0, 4, 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 0, 5, 0, 0, 0},{0, 6, 0, 0, 8, 0, 0, 7, 3},{0, 0, 0, 8, 0, 1, 9, 0, 0},{6, 5, 0, 0, 0, 0, 0, 0, 0},{0, 0, 0, 3, 0, 0, 0, 0, 8},{0, 2, 0, 0, 3, 0, 0, 0, 7},{0, 0, 0, 0, 0, 7, 1, 3, 0},{4, 7, 0, 0, 0, 0, 8, 9, 0},}

思考和优化

人在做这个的时候,会先把容易填的填完,不会按照顺序说一定要先填哪一个,尝试在这儿进行如下优化

  1. 不按顺序填,遍历所有需要填的空格,当某个空格只有唯一选项时,直接填,填了就是一个新的数独了,如果没有唯一选项的空格时,依次挑选唯二,唯三,唯四选项的空格进行尝试

代码实现

package mainimport ("fmt""time"
)func shuzu_print(shuzu [9][9]int) {fmt.Println("------------------------")for i := 0; i < 9; i++ {fmt.Println(shuzu[i])}
}var num int = 0 // 记录一下函数执行的次数func main() {shuzu := [9][9]int{{0, 0, 0, 0, 0, 0, 0, 0, 0},{5, 9, 0, 8, 0, 0, 7, 0, 0},{0, 0, 0, 0, 2, 1, 8, 0, 0},{0, 3, 7, 0, 0, 0, 0, 0, 0},{0, 5, 0, 7, 9, 0, 0, 0, 0},{0, 0, 0, 0, 3, 0, 1, 8, 0},{0, 0, 5, 0, 0, 2, 0, 0, 0},{8, 1, 0, 0, 0, 0, 0, 4, 0},{0, 0, 6, 0, 8, 0, 9, 0, 3},}shuzu_print(shuzu)t1 := time.Now()fmt.Println(t1)result := handle2(shuzu)shuzu_print(result)fmt.Println(time.Now().Sub(t1)) // 记录一下花费的时间}type Left struct { // 定义一个结构体,表示i行j列剩余可以填的数字i    intj    intlen  intleft []int
}func check(shuzu [9][9]int) bool {// 检查数组有没有填完for i := 0; i < 9; i++ {for j := 0; j < 9; j++ {if shuzu[i][j] == 0 {return false}}}return true}
// 这个里面还用了goto,loop ,让函数有个返回
func handle2(shuzu [9][9]int) [9][9]int {num += 1left_map := map[int]Left{}if check(shuzu) {println("hhhhhhhhhhhhhhhh")// 填完了,真开心hhhhhshuzu_print(shuzu)fmt.Println(num) // 看一下该函数执行了多少次return shuzu}new_shuzu := shuzufor i := 0; i < 9; i++ {for j := 0; j < 9; j++ {if shuzu[i][j] != 0 {continue}set := make(map[int]struct{}) // 同行同列同小组已经存在的数字for k := 0; k < 9; k++ {if shuzu[i][k] != 0 {set[shuzu[i][k]] = struct{}{}}}for k := 0; k < 9; k++ {if shuzu[k][j] != 0 {set[shuzu[k][j]] = struct{}{}}}i_3 := i / 3j_3 := j / 3for ii := i_3 * 3; ii < (i_3+1)*3; ii++ {for jj := j_3 * 3; jj < (j_3+1)*3; jj++ {if shuzu[ii][jj] != 0 {set[shuzu[ii][jj]] = struct{}{}}}}left_len := 9 - len(set)if left_len == 0 {goto Loop // 出现矛盾的时候,跳出执行}if left_len == 1 { // 只剩一个可填,直接处理for kk := 1; kk < 10; kk++ {if _, ok := set[kk]; !ok {shuzu[i][j] = kknew_shuzu = handle2(shuzu)goto Loop// 直接跳出循环了}}} else {_, ok := left_map[left_len]if !ok {left_num := []int{}for kk := 1; kk < 10; kk++ {if _, ok := set[kk]; !ok {left_num = append(left_num, kk)}}left_map[left_len] = Left{i, j, left_len, left_num} //对于每种剩余可填长度,记录一个即可}}}}for k := 2; k < 10; k++ { // 依次找剩余可填长度为2,3,4....,找到一个即可left, ok := left_map[k]if ok {for _, value := range left.left {shuzu[left.i][left.j] = valuenew_shuzu = handle2(shuzu)if check(new_shuzu) {goto Loop}}break}}
Loop:// fmt.Println("跳出循环")return new_shuzu
}

运行结果:函数执行3760次,10ms运行完成,基本满意,终于不用我想破脑壳做一个小时了

问题及优化

  1. 代码写的很粗糙,命名也是随手写的,理解哈
  2. 其实人在做数独的时候,不仅可以看到每个空格可填的数字,还可以看到可排除了,根据所在小九宫格其他的可填和可排除的,这样的话,可以进一步减少函数执行的次数。
  3. 这里面9*9 是用数组,对于go来说,数组作为函数的参数是值传递,也就是说9*9 的数组,复制了3760次,如果用切片的话,可以节省这个复制,但是用切片的话,如果试错了,往回走的整个链路都要进行恢复,这个想一想应该还是可以优化出来的
  4. 各位大佬,如果还有可以优化的点,欢迎赐教

这篇关于go 实现暴力破解数独的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/653299

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

基于Springboot + vue 的抗疫物质管理系统的设计与实现

目录 📚 前言 📑摘要 📑系统流程 📚 系统架构设计 📚 数据库设计 📚 系统功能的具体实现    💬 系统登录注册 系统登录 登录界面   用户添加  💬 抗疫列表展示模块     区域信息管理 添加物资详情 抗疫物资列表展示 抗疫物资申请 抗疫物资审核 ✒️ 源码实现 💖 源码获取 😁 联系方式 📚 前言 📑博客主页:

探索蓝牙协议的奥秘:用ESP32实现高质量蓝牙音频传输

蓝牙(Bluetooth)是一种短距离无线通信技术,广泛应用于各种电子设备之间的数据传输。自1994年由爱立信公司首次提出以来,蓝牙技术已经经历了多个版本的更新和改进。本文将详细介绍蓝牙协议,并通过一个具体的项目——使用ESP32实现蓝牙音频传输,来展示蓝牙协议的实际应用及其优点。 蓝牙协议概述 蓝牙协议栈 蓝牙协议栈是蓝牙技术的核心,定义了蓝牙设备之间如何进行通信。蓝牙协议

python实现最简单循环神经网络(RNNs)

Recurrent Neural Networks(RNNs) 的模型: 上图中红色部分是输入向量。文本、单词、数据都是输入,在网络里都以向量的形式进行表示。 绿色部分是隐藏向量。是加工处理过程。 蓝色部分是输出向量。 python代码表示如下: rnn = RNN()y = rnn.step(x) # x为输入向量,y为输出向量 RNNs神经网络由神经元组成, python

利用Frp实现内网穿透(docker实现)

文章目录 1、WSL子系统配置2、腾讯云服务器安装frps2.1、创建配置文件2.2 、创建frps容器 3、WSL2子系统Centos服务器安装frpc服务3.1、安装docker3.2、创建配置文件3.3 、创建frpc容器 4、WSL2子系统Centos服务器安装nginx服务 环境配置:一台公网服务器(腾讯云)、一台笔记本电脑、WSL子系统涉及知识:docker、Frp

基于 Java 实现的智能客服聊天工具模拟场景

服务端代码 import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;public class Serv