go 实现暴力破解数独

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

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

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

代码实现

package main

import (
	"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 / 3
			j_3 := j / 3
			for 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] = kk
					handle(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 main

import (
	"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    int
	j    int
	len  int
	left []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 += 1
	left_map := map[int]Left{}

	if check(shuzu) {
		println("hhhhhhhhhhhhhhhh")// 填完了,真开心hhhhh
		shuzu_print(shuzu)
		fmt.Println(num) // 看一下该函数执行了多少次
		return shuzu
	}
	new_shuzu := shuzu
	for 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 / 3
			j_3 := j / 3
			for 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] = kk
						new_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] = value
				new_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. 各位大佬,如果还有可以优化的点,欢迎赐教

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/353748.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【NodeJS JS】动态加载字体的各方式及注意事项;

首先加载字体这个需求基本只存在于非系统字体&#xff0c;系统已有字体不需要加载即可直接使用&#xff1b; 方案1&#xff1a;创建 style 标签&#xff0c;写入 font-face{font-family: xxx;src: url(xxx)} 等相关字体样式&#xff1b;将style标签添加到body里&#xff1b;方…

2024017期传足14场胜负前瞻

2024017期赛事由亚洲杯2场、英总杯2场、德甲2场、意甲4场、西甲4场组成。售止时间为1月28日&#xff08;周日&#xff09;19点00分&#xff0c;敬请留意&#xff1a; 本期深盘场次同样适中&#xff0c;1.5以下赔率3场&#xff0c;1.5-2.0赔率6场&#xff0c;其他场次基本皆是平…

武汉大学齐民友教授简介

齐民友&#xff08;1930年2月—2021年8月8日&#xff09;&#xff0c;男&#xff0c;出生于安徽省芜湖市&#xff0c;中国共产党优秀党员&#xff0c;数学家、教育家、偏微分方程专家&#xff0c;武汉大学原校长、数学与统计学院教授、博士生导师 。 齐民友于1948年考入武汉大…

(南京观海微电子)——OLED驱动与调试

一、OLED DDIC分类 OLED DDIC的技术方向可以分为3类&#xff1a;带Ram【内存】的IC、Ram-less IC和TDDI【显示&触控集成的IC】 1、带Ram的OLED DDIC OLED DDIC有两个Ram&#xff0c;分别是Demura Ram和Display Ram。 1、带Ram的OLED DDIC 1-1&#xff09;Demura Ram&a…

课时6:编程语言逻辑

1.2.2 编程语言逻辑 学习目标 这一节&#xff0c;我们从 语言分类、编程逻辑、小结 三个方面来学习。 语言分类 语言分类 低级编程语言&#xff1a;机器&#xff1a;- 二进制的0和1的序列&#xff0c;称为机器指令。- 一般人看不懂汇编&#xff1a;- 用一些助记符号替代机…

Linux ---- Shell编程之函数与数组

目录 一、函数 1、函数的基本格式 2、查看函数列表 3、删除函数 4、函数的传参数 5、函数返回值 实验&#xff1a; 1.判断输入的ip地址正确与否 2. 判断是否为管理员用户登录 6、函数变量的作用范围 7、函数递归&#xff08;重要、难点&#xff09; 实验&#xff1…

山西电力市场日前价格预测【2024-01-28】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-01-28&#xff09;山西电力市场全天平均日前电价为280.26元/MWh。其中&#xff0c;最高日前电价为556.88元/MWh&#xff0c;预计出现在18:15。最低日前电价为0.00元/MWh&#xff0c;预计出…

智能分析网关V4智慧机房:视频AI智能安全监管方案

一、背景分析 随着互联网的迅猛发展&#xff0c;机房及其配套设施的数量持续攀升&#xff0c;它们的运行状况对于企业运营效率和服务质量的影响日益显著。作为企业信息化的基石&#xff0c;机房的安全监测与管理的重要性不容忽视。它不仅关乎企业的稳定运营&#xff0c;同时也直…

Android Studio 提示Use app:drawableStartCompat instead of android:drawableStart

每次提交代码时&#xff0c;AS这个老妈子总爱唠叨一堆warning&#xff0c;这些Warning都在讲什么&#xff1f; 1.Use app:drawableStartCompat instead of android:drawableStart 在Android开发中&#xff0c;android:drawableStart和app:drawableStartCompat是两个用于设置…

Java多线程基础-18:线程安全的集合类与ConcurrentHashMap

Java标准库提供了很多集合类&#xff0c;但有一些集合类是线程不安全的&#xff0c;也就是说&#xff0c;在多线程环境下可能会出问题的。常用的ArrayList&#xff0c;LinkedList&#xff0c;HashMap&#xff0c;PriorityQueue等都是线程不安全的&#xff08;Vector, Stack, Ha…

【C语言/数据结构】排序(选择排序,推排序,冒泡排序)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;《数据结构》https://blog.csdn.net/qinjh_/category_12536791.html?spm1001.2014.3001.5482 ​​​​ 目录 选择排序 选择排序 ​编辑…

【开源】基于JAVA语言的学生综合素质评价系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生功能2.2 教师功能2.3 教务处功能 三、系统展示四、核心代码4.1 查询我的学科竞赛4.2 保存单个问卷4.3 根据类型查询学生问卷4.4 填写语数外评价4.5 填写品德自评问卷分 五、免责说明 一、摘要 1.1 项目介绍 基于J…

美睫师睫毛嫁接零基础学习,日式美睫与开花嫁接实战教学

一、教程描述 大家都说女人的钱好挣&#xff0c;这是因为每个女人在每年&#xff0c;都要花很多钱来打扮自己。本套教程是关于日式美睫和开花嫁接的&#xff0c;从零基础学习到店铺经营都有涉及&#xff0c;就做美睫和睫毛嫁接这两项业务&#xff0c;月收入万元以上应该问题不…

ubuntu 22.04 安装mysql-8.0.34

ubuntu 22.04 安装mysql-8.0.34 1、基础安装配置 更新软件包&#xff1a; sudo apt update查看可用软件包&#xff1a; sudo apt search mysql-server安装最新版本&#xff1a; sudo apt install -y mysql-server或者&#xff0c;安装指定版本&#xff1a; sudo apt inst…

202|读书笔记《金融的本质:伯南克四讲美联储》

今天跟朋友聊天&#x1f4ac;&#xff0c;说已经没人看书了&#x1f4d6; 我想&#xff0c; 还是会有人读书的吧。 ​ 一、美联储的起源和使命 1. 第一讲&#xff1a;美国南北战争结束后的40年间&#xff0c;美国经历了6次大的银行体系恐慌&#xff0c;促使其于1913年成立美联储…

第八篇【传奇开心果系列】beeware的toga开发移动应用示例:实现消消乐安卓手机小游戏

传奇开心果博文系列 系列博文目录beeware的toga开发移动应用示例系列博文目录一、项目目标二、安装依赖三、初步实现四、扩展思路五、实现游戏逻辑示例代码六、实现界面设计示例代码七、实现增加关卡和难度示例代码八、实现存档和排行榜示例代码九、实现添加特殊方块和道具示例…

C语言第十一弹---函数(下)

​ ✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 函数 1、嵌套调用和链式访问 1.1、嵌套调用 1.2、链式访问 2、函数的声明和定义 2.1、单个文件 2.2、多个文件 2.3、static 和 extern 2.3.1、static…

第六课:Prompt

文章目录 第六课&#xff1a;Prompt1、学习总结&#xff1a;Prompt介绍预训练和微调模型回顾挑战 Pre-train, Prompt, PredictPrompting是什么?prompting流程prompt设计 课程ppt及代码地址 2、学习心得&#xff1a;3、经验分享&#xff1a;4、课程反馈&#xff1a;5、使用Mind…

08. BI - 万字长文,银行如何做贷款违约的预测,特征处理及学习

本文为 「茶桁的 AI 秘籍 - BI 篇 第 08 篇」 文章目录 课程回顾案例分析案例实战 Hi&#xff0c; 你好。我是茶桁。 课程回顾 上节课&#xff0c;咱们讲了一个股票的指标&#xff1a;MACD。在趋势行情里面它应该还是有效的指标。它比较忌讳动荡行情&#xff0c;比如说它一会上…

###C语言程序设计-----C语言学习(5)#

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步&#xff01; 一. 主干知识的学习 1.switch语句 switch语句可以处理多分支选…