Python生成数独题目 

闲暇之余,产生另外一个想法:用Python生成数独题目。

经典数独是9*9的方阵。最简单的生成题目思路是按照从左至右,从上至下的顺序逐个把1~9的数字随机填入到数独的格子中。然后再随机抹去一定数量的格子即可。

当然,这里随机填入的时候,还要考虑到数独的规则:

1)同一行的数字不能重复

2)同一列的数字不能重复

3)同一个九宫格的数字不能重复

所以,在随机填入的时候,每次填入之前都要检查是否符合上面的条件。或者在随机生成数字的时候,就把所在格子的行、列、九宫格已经存在的数字排除在外,然后再生成随机数字。

我采取排除的方法生成随机数字,简单代码如下:

1)先定义获取一个二维矩阵行、列、九宫格的方法

importrandomimportnumpyasnp#其中sudo参数是numpy的二维矩阵#获取格子所在的行的全部格子defget_row(sudo,row):returnsudo[row,:]#获取格子所在的列的全部格子defget_col(sudo,col):returnsudo[:,col]#获取格子所在的九宫格的全部格子defget_block(sudo,row,col):row_start=row//3*3col_start=col//3*3returnsudo[row_start:row_start+3,col_start:col_start+3]

2)书写生成逻辑:遍历,排除已被使用的数字,随机填入

3)执行

我尝试执行,都遇到没有数字可用的错误

调整代码,打印发生错误时的数独

再次执行:

执行结果,第2行第9列没有数字可用。

我们看第2行已经几乎填满了数字,剩下4可填。而在这个九宫格,数字4已经被使用(第1行第8列)。所以无数字可用。

4)初步结论

后来,我又尝试了好几次,都是出现这种结果。仔细思考,得出以下结论:

以上面的例子为例,第二行在随机填写数字的时候,没有考虑到数字4在后面3列无法使用。除了数独基本3个限制条件外,其实这3个限制条件会导致数字之间产生一种纠缠关系。而前面简单的做法没有考虑到这种潜在的影响,所以无法生成一个完整的数独。

从数独的3个限制条件出发,3个限制条件都是不同的维度。而这些维度都有交叉关系,例如一个格子同时存在于某一行和某一列、某一九宫格。就像空间坐标轴,空间上的任意一点都有x、y、z等3个坐标轴。

感觉数独是一种高纬度的矩阵,我们看到的二维矩阵只是高纬度矩阵在二维上的投影。如果用这种简单随机方法生成数独的话,很难涉及到每个数字之间的纠缠关系,可能需要用更高维度的方式去处理,例如3维矩阵、4维矩阵。

后来,我尝试提高维度去解决问题,也遇到同样无法直接生成数独。可能我的数学知识还不够,不能用数学的方式表达数独。

在不断翻阅资料时,偶然发现一个规律:在已有的数独结果上,同一个九宫格内任意两个格子,调换其所在的行或者所在的列后的结果,还是一个有效的数独。

例如交换D1和E3两个格子所在的行:

数独3个限制条件都没有被破坏。进一步发现,其实只要在九宫格范围里面的任意两行或两列交换都可以。

例如交换D行和E行,D行和E行都在九宫格的范围内。而A行和D行就不能交换。

如此一来,我们只要用一个简单的数独(这个术语叫基本盘),随机多次交换不会破坏数独完整性的行或列,形成最终结果(术语叫终盘);最后再随机抹去一些数据即可变成数独题目。

1)生成基本盘

生成基本盘的代码如下(延用上面的get_row、get_col、get_block方法):

defcreate_base_sudo():#9*9的二维矩阵,每个格子默认值为0sudo=np.zeros((9,9),dtype=int)#随机生成起始的基数(1~9)num=random.randrange(9)+1#遍历从左到右,从上到下逐个遍历forrow_indexinrange(9):forcol_indexinrange(9):#获取该格子对应的行、列、九宫格sudo_row=get_row(sudo,row_index)sudo_col=get_col(sudo,col_index)sudo_block=get_block(sudo,row_index,col_index)#如果该数字已经存在于对应的行、列、九宫格#则继续判断下一个候选数字,直到没有重复whilenuminsudo_rowornuminsudo_colornuminsudo_block:num=num%9+1#赋值sudo[row_index,col_index]=numnum=num%9+1returnsudo

这里用了求余的方法,把num限制在1到9之间。执行该方法,可以得到如下类似的基本盘:

2)随机交换,得到终盘

然后我们再多次随机交换行列,得到终盘:

#随机交换n次defrandom_sudo(sudo,times):for_inrange(times):#随机交换两行rand_row_base=random.randrange(3)*3#从0,3,6随机取一个rand_rows=random.sample(range(3),2)#从0,1,2中随机取两个数row_1=rand_row_base+rand_rows[0]row_2=rand_row_base+rand_rows[1]sudo[[row_1,row_2],:]=sudo[[row_2,row_1],:]#随机交换两列rand_col_base=random.randrange(3)*3rand_cols=random.sample(range(3),2)col_1=rand_col_base+rand_cols[0]col_2=rand_col_base+rand_cols[1]sudo[:,[col_1,col_2]]=sudo[:,[col_2,col_1]]

我随机交换50次,最终得到一个和基本盘差别很大的终盘:

3)随机抹去数字,得到数独题目

这里如果抹去数字太多,会导致题目很难解,或者容易有多个解;如果抹去数字太少,数独题目就会变得很简单。

经过一番测试,大概最多清除64个数字,最少清除14个数字,得到的题目可解。代码如下:

defget_sudo_subject(sudo,del_nums):#最少要保留17个数字,题目才可解。一共81个数字,则最多可以擦除81-17=64。max_clear_count=64#最少也要擦除14个min_clear_count=14ifdel_numsmax_clear_count:del_nums=max_clear_count#随机擦除(从0到80,随机取要删除的个数)clears=random.sample(range(81),del_nums)forclear_indexinclears:#把0到80的坐标转化成行和列索引#这样就不会重复删除同一个格子的数字row_index=clear_index//9col_index=clear_index%9sudo[row_index,col_index]=0

下面是我随机抹去30个数字的效果:

4)整合代码

这里除了整合全部代码之外,我还划分了难度等级,抹去数字越多则越难。我将其划分为5个级别:入门、初级、熟练、精通、大神。

THE END
1.人生建议,不妨先尝试着开始比如最近学习剪辑了几个短视频,开始运营自己的公众号,学习写小说、练字,其实这些东西之前都尝试过,除了没时间没毅力之外,我总是一边做一边觉得自己还没有准备好,故而把心思全放在了“准备"上了。 其实人永远都不会准备好,过度的准备就预示着荒废。 所以,不妨先试着开始,别怕自己“三分钟热度”,三分钟热度也有三https://www.jianshu.com/p/387f7ed8f403
2.第一次是挑战,也带给我们成长与喜悦。说一说你都有哪些第一次?发布:2024/12/11 12:30:1组卷:3引用:2难度:0.5 解析 2.在大胆尝试的过程中可以不讲究方法。 (判断对错) 发布:2024/12/11 15:30:1组卷:0引用:2难度:0.5 解析 3.你喜欢尝试哪些新鲜事物?大胆说出来,说错了也没关系的。 (判断对错) 发布:2024/12/11 5:30:2组卷:6引用:4难度:0.5 解析把https://www.jyeoo.com/shiti/010c8c87-15ac-4215-5f0e-90c3a6543725
3.初次尝试最新版下载初次尝试游戏下载v1.11初次尝试是一款创意感十足的休闲闯关趣味游戏,玩家不断的收集钻石来升级道具,拾取的金币越多,所获得的奖励也会大大的提升,游戏前期简单,后期飞机前进的速度会变快,还会有很多大型飞机需要玩家击毁!字啊商城内还可以购买海量飞机皮肤,提升自身的战斗力,适合休闲的时候体验!喜欢的小伙伴快来畅玩吧! http://www.dianwannan.com/app/1000000113/
4.新手(美国2021年LaurenHadaway导演的电影)《新手》是劳伦·哈达威执导,伊莎贝拉·弗尔曼和艾米·福赛思出演的剧情片。该片讲述了艾历克斯·道尔在加入了学校的赛艇部之后,不惜任何代价都要打败队友、进入专业队代表学校参加比赛的故事。剧情简介 艾历克斯·道尔是一个追求极致的大一新生,会为了考出更高分反复参加考试。在加入了学校的赛艇部之后,她不惜任何https://baike.baidu.com/item/%E6%96%B0%E6%89%8B/59388631
5.初次尝试其他视频其他视频:精彩视频https://m.tv.sohu.com/v/dXMvMjAxMzM3NTI1LzUzMDUzODE5LnNodG1s.html
6.大班教案精初次尝试:提供大小对比较明显的芝麻与黄豆,幼儿在尝试使用各种工具后,发现使用筛子能很快将两种混在一起的东西分开。该环节让幼儿在自主操作中发现筛子的作用。 第二次尝试:提供芝麻和盐、绿豆和花生,分离时仅需要用到大、小孔筛子,中孔筛子的提供为这一环节的操作增加了阻碍,通过这样的操作探索,幼儿的经验进一步提升https://www.wenshubang.com/jiaoan/3842106.html
7.运动小白练习CrossFit半个月浅谈初次尝试 第一次上课真的有被吓到,一来上一节课的学员看起来都很累的样子,二来场馆里散落的哑铃杠铃都让人觉得重量无比巨大。好在看到好几个女生仍然是看起来很“正常“的样子,没有孔武有力的肌肉女。 然后那节课的套路(术语叫WOD:workoutof the day)是两个人轮流做四轮动作,要求在规定的时间内做完10组https://www.douban.com/note/812058370/
8.java红包接口开发java实现拼手气红包Java微信手气红包实现 前言 昨天坐公交车,微信群里在发红包,突然想到在思考一个问题:微信的手气红包的算法是如何实现的 闲的没事开始尝试起来了 初次尝试 一开始的想法很简单,就是固定一个奖金池,因为要保证每个最低是有一分钱,所以把随机数的总金额减去剩下每人的0.01作为最高可以获取的红包金额,接着把奖金池减去https://blog.51cto.com/u_16099172/7321209
9.某音短视频APP最新版(21.8)SSLPINNING绕过抖音sslpinning抓包初次尝试 开始下载了5.0版的安卓模拟器,下载了比较旧的某音APP 17.3版本,发现是可以抓到包的,但是无法登陆,提示版本过低。所以只能升级APP版本,但升级到最新版发现之前的抓包工具无法抓到包了,提示 SSL Handshake Failed。 SSL Pinning [SSL Pinning]分为两种: https://blog.csdn.net/jx_ZhangZhaoxuan/article/details/124742871
10.后悔染黑茶色的经历:我为何如此决定人生中总会有一些决定让我们后悔不已,而我曾经决定染黑茶色头发的经历就是其中之一。这个决定看似微不足道,却给我带来了长期的困扰和后悔。 初次尝试 记得那是大学时代,年轻而充满好奇心的我对各种新鲜事物都充满了向往。在一个偶然的机会下,我看到了一位明星拥有漂亮黑茶色头发的照片。被她那迷人而神秘的形象所https://www.coffee.cn/tea/post/38773.html
11.别再瞎喂了!0~3岁宝宝每天该吃啥,吃多少,一篇告诉你初次尝试只要吃一勺或半勺,吃的量上去了再增加; 可以从含铁的糊状食物(米粉)开始,每次尝试一种食物,观察3天左右,避免食物过敏。 >>>7~9个月 可以安排2~3次辅食,食物性状变成了菜碎、肉末,比之前稠,锻炼宝宝的吞咽和咀嚼能力。 鱼类、肝脏一周安排1-2次,宝宝能接受蛋黄的话,可以试一点蛋白,https://3g.yihu.com/index.php/act/daily_health/detail.html?platformType=4&article_id=86d30daf88d5dd05abcbe8575a705b44