前几天和隔壁邻居玩斗地主被发现了,牌被没收了,斗地主是斗不了了,但我还想和邻居玩耍。如果你还想斗斗地主,戳:趁老王不在,和隔壁邻居斗斗地主,比比大小

想破脑袋终于让我想到一个游戏,数独!什么叫数独?数独就是可以让我趁老王不在的时候和隔壁邻居一起玩耍的游戏!

数独的规则

1、数字 1-9 在每一行只能出现一次。

2、数字 1-9 在每一列只能出现一次。

3、数字 1-9 在每一个 3x3 宫内只能出现一次。3x3 的宫内为A1-C3,A4-C6,A7-C9,D1-F3,D4-F6,D7-F9...

数独题目示例

大致思路

1、数独我们使用一个二维列表存储,没有值的位置我们使用''空字符窜占位。(二维数组)

2、得到每一个3*3的宫内,每一行,每一列已有的数据,然后存放起来。3、得到所有的空缺位置,再遍历空缺位置,尝试放置数据,然后进行判断,如果满足条件安继续放置下一个。以此类推,在途中有不满足条件的情况,就进行回溯,返回上一次满足条件的情况,在进行另一次尝试。

演示环境

  • 操作系统:windows10
  • python版本:python 3.7
  • 代码编辑器:pycharm 2018.2

具体代码

1、首选我们创建一个类SudoKu。编写构造函数。

class SudoKu():
def __init__(self,sudo_ku_data):
# 判断传入的数独是否满足格式
if not isinstance(sudo_ku_data,list):
raise TypeError(f'sudo_ku_data params must a list, but {sudo_ku_data} is a {type(sudo_ku_data)}') if len(sudo_ku_data) != 9 or len(sudo_ku_data[0]) != 9:
raise TypeError(f'sudo_ku_data params must a 9*9 list, but {sudo_ku_data} is a {len(sudo_ku_data)}*{len(sudo_ku_data[0])} list') self.sudo_ku = sudo_ku_data
# 存放每一行已有的数据
self.every_row_data = {}
# 每一列已有的数字
self.every_column_data = {}
# 每一个3*3宫内有的数字
self.every_three_to_three_data = {}
# 每一个空缺的位置
self.vacant_position = []
# 每一个空缺位置尝试了的数字
self.every_vacant_position_tried_values = {}

2、编写添加每一行,每一列,每一宫方法,方便我们后面调用

def _add_row_data(self,row,value):
'''
添加数据到self.every_row_data中,即对每一行已有的数据进行添加
:param row:
:param value:
:return:
'''
# 如果当前行不存在,就以当前行为key,初始化值为set()(空的集合)
if row not in self.every_row_data:
self.every_row_data[row] = set() # 如果这个值已经出现过在这一行了,说明传入的不是一个正确的数独
if value in self.every_row_data[row]:
raise TypeError(f'params {self.sudo_ku} is a invalid SudoKu') self.every_row_data[row].add(value) def _add_column_data(self,column,value):
'''
添加数据到self.every_column_data中,上面的函数思路一样
:param column:
:param value:
:return:
'''
if column not in self.every_column_data:
self.every_column_data[column] = set() if value in self.every_column_data[column]:
raise TypeError(f'params {self.sudo_ku} is a invalid SudoKu') self.every_column_data[column].add(value) def _get_three_to_three_key(self,row,column):
'''
得到该位置在哪一个3*3的宫内
:param row:
:param column:
:return:
'''
if row in [0,1,2]:
if column in [0,1,2]:
key = 1
elif column in [3,4,5]:
key = 2
else:
key = 3
elif row in [3,4,5]:
if column in [0,1,2]:
key = 4
elif column in [3,4,5]:
key = 5
else:
key = 6
else:
if column in [0,1,2]:
key = 7
elif column in [3,4,5]:
key = 8
else:
key = 9 return key def _add_three_to_three_data(self,row,column,value):
'''
添加数据到self.every_three_to_three_data中
:param row:
:param column:
:param value:
:return:
'''
# 首先得到在哪一个3*3的宫内
key = self._get_three_to_three_key(row,column) # 然后也和上面添加行,列的思路一样
if key not in self.every_three_to_three_data:
self.every_three_to_three_data[key] = set() if value in self.every_three_to_three_data[key]:
raise TypeError(f'params {self.sudo_ku} is a invalid SudoKu') self.every_three_to_three_data[key].add(value)

3、遍历数独,对每种数据进行初始化

def _init(self):
'''
根据传入的数独,初始化数据
:return:
'''
for row,row_datas in enumerate(self.sudo_ku):
for column,value in enumerate(row_datas):
if value == '':
# 添加空缺位置
self.vacant_position.append( (row,column) )
else:
# 添加行数据
self._add_row_data(row,value)
# 添加列数据
self._add_column_data(column,value)
# 添加宫数据
self._add_three_to_three_data(row,column,value)

4、编写判断某一个位置的值是否合法的函数

def _judge_value_is_legal(self,row,column,value):
'''
判断方放置的数据是否合法
:param row:
:param column:
:param value:
:return:
''' # value是否存在这一行数据中
if value in self.every_row_data[row]:
return False
# value是否存在这一列数据中
if value in self.every_column_data[column]:
return False # value是否存在这个3*3的宫内
key = self._get_three_to_three_key(row,column)
if value in self.every_three_to_three_data[key]:
return False return True

5、编写计算的函数,在当前位置循环 可以使用的额数据,确定可以是否可以放置这个值

def _calculate(self, vacant_position):
'''
计算,开始对数独进行放置值
:param vacant_position:
:return:
'''
# 得到当前位置
row,column = vacant_position
values = set(range(1,10)) # 对当前为位置创建一个唯一key,用来存放当前位置已经尝试了的数据
key = str(row) + str(column)
# 如果这个key存在,就对values进行取差集,因为两个都是集合(set),直接使用-就行了
if key in self.every_vacant_position_tried_values:
values = values - self.every_vacant_position_tried_values[key]
# 如果这个key不存在,就创建一个空的集合
else:
self.every_vacant_position_tried_values[key] = set() for value in values:
# 对当前数据添加到当前位置尝试过的的数据中
self.every_vacant_position_tried_values[key].add(value)
# 如果当前value合法,可以放置
if self._judge_value_is_legal(row,column,value):
print(f'set {vacant_position} value is {value}')
# 更新 判断数据合法时 需要使用到的数据
self.every_column_data[column].add(value)
self.every_row_data[row].add(value)
key = self._get_three_to_three_key(row,column)
self.every_three_to_three_data[key].add(value) # 修改这个位置的值为value
self.sudo_ku[row][column] = value
# 返回True 和填充的 value
return True,value return False,None

6、如果当前位置没有任何一个值可以放置,那么就回溯,返回上一次成功的位置,重新取值,所以我们编写一个回溯函数

def _backtrack(self,current_vacant_position,previous_vacant_position,previous_value):
'''
回溯
:param current_vacant_position: 当前尝试失败的位置
:param previous_vacant_position: 上一次成功的位置
:param previous_value:上一次成功的值
:return:
'''
print(f"run backtracking... value is {previous_value},vacant position is {previous_vacant_position}")
row,column = previous_vacant_position
# 对上一次成功的值从需要用到的判断的数据中移除
self.every_column_data[column].remove(previous_value)
self.every_row_data[row].remove(previous_value) key = self._get_three_to_three_key(row,column)
self.every_three_to_three_data[key].remove(previous_value) # 并且上一次改变的的值变回去
self.sudo_ku[row][column] = '' # 对当前尝试失败的位置已经城市失败的的值进行删除,因为回溯了,所以下一次进来需要重新判断值
current_row,current_column = current_vacant_position
key = str(current_row) + str(current_column)
self.every_vacant_position_tried_values.pop(key)

7、到这里为止,我们所有的功能函数都写完了,然后我们编写一个函数,开始循环所有的空缺位置。然后进行计算。

def get_result(self):
'''
得到计算之后的数独
:return:
''' # 首先初始化一下数据
self._init() # 空缺位置的长度
length = len(self.vacant_position)
# 空缺位置的下标
index = 0 # 存放已经尝试了的数据
tried_values = []
# 如果index小于length,说明还没有计算完
while index < length:
# 得到一个空缺位置
vacant_position = self.vacant_position[index] # 计入计算函数,返回是否成功,如果成功,value为成功 的值,如果失败,value为None
is_success,value = self._calculate(vacant_position)
# 如果成功,将value放在tried_values列表里面,因为列表是有序的.
# index+1 对下一个位置进行尝试
if is_success:
tried_values.append(value)
index += 1
# 失败,进行回溯,并且index-1,返回上一次的空缺位置,我们需要传入当前失败的位置 和 上一次成功的位置和值
else:
self._backtrack(vacant_position,self.vacant_position[index-1],tried_values.pop())
index -= 1 # 如果index<0 了 说明这个数独是无效的
if index < 0:
raise ValueError(f'{self.sudo_ku} is a invalid sudo ku') # 返回计算之后的数独
return self.sudo_ku

效果展示

呼。。。终于干完代码,接下来我们呢可以"开始收获"了

if __name__ == '__main__':
sudo_ku_data = [
[5,3,'','',7,'','','',''],
[6,'','',1,9,5,'','',''],
['',9,8,'','','','',6,''],
[8,'','','',6,'','','',3],
[4,'','',8,'',3,'','',1],
[7,'','','',2,'','','',6],
['',6,'','','','',2,8,''],
['','','',4,1,9,'','',5],
['','','','',8,'','',7,9],
] # 得到计算好的数独
sudo_ku = SudoKu(sudo_ku_data).get_result()
print(sudo_ku) ################
# 结果显示 #
################
[5, 3, 4, 6, 7, 8, 9, 1, 2]
[6, 7, 2, 1, 9, 5, 3, 4, 8]
[1, 9, 8, 3, 4, 2, 5, 6, 7]
[8, 5, 9, 7, 6, 1, 4, 2, 3]
[4, 2, 6, 8, 5, 3, 7, 9, 1]
[7, 1, 3, 9, 2, 4, 8, 5, 6]
[9, 6, 1, 5, 3, 7, 2, 8, 4]
[2, 8, 7, 4, 1, 9, 6, 3, 5]
[3, 4, 5, 2, 8, 6, 1, 7, 9]

这效果就很完美啊,我们在来测试一个比较难得数独。

输入数独为:

[
[8, '', '', '', '', '', '', '', 4],
['', 2, '', '', '', '', '', 7, ''],
['', '', 9, 1, '', 6, 5, '', ''],
['', '', 6, 2, '', 8, 9, '', ''],
['', 9, '', '', 3, '', '', 4, ''],
['', '', 2, 4, '', 7, 8, '', ''],
['', '', 7, 9, '', 5, 6, '', ''],
['', 8, '', '', '', '', '', 2, ''],
[6, '', '', '', '', '', '', '', 9],
] ################
# 结果显示 #
################
[8, 6, 1, 5, 7, 2, 3, 9, 4]
[5, 2, 4, 3, 8, 9, 1, 7, 6]
[3, 7, 9, 1, 4, 6, 5, 8, 2]
[4, 3, 6, 2, 5, 8, 9, 1, 7]
[7, 9, 8, 6, 3, 1, 2, 4, 5]
[1, 5, 2, 4, 9, 7, 8, 6, 3]
[2, 4, 7, 9, 1, 5, 6, 3, 8]
[9, 8, 5, 7, 6, 3, 4, 2, 1]
[6, 1, 3, 8, 2, 4, 7, 5, 9]

哈哈哈哈哈,以后还有谁能够和我比解数独。膨胀.jpg

代码已全部上传至Github:https://github.com/MiracleYoung/You-are-Pythonista/tree/master/PythonExercise/App/solveSudoku/xujin

更多好玩有趣的Python尽请关注「Python专栏

旁友数独会伐啦?python秒解数独了解下伐啦?的更多相关文章

  1. 解数独(Python)

    0.目录 1.介绍 2.一些通用函数 3.全局变量(宏变量) 4.数独预处理(约束传播) 5.解数独(深度优先搜索+最小代价优先) 6.主函数 7.总代码 1.介绍 数独是一个非常有趣味性的智力游戏, ...

  2. 使用python解数独

    偶然发现linux系统附带的一个数独游戏,打开玩了几把.无奈是个数独菜鸟,以前没玩过,根本就走不出几步就一团浆糊了. 于是就打算借助计算机的强大运算力来暴力解数独,还是很有乐趣的. 下面就记录一下我写 ...

  3. 孤荷凌寒自学python第四十五天Python初学基础基本结束的下阶段预安装准备

     孤荷凌寒自学python第四十五天Python初学基础基本结束的下阶段预安装准备 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 今天本来应当继续学习Python的数据库操作,但根据过去我自 ...

  4. < python音频库:Windows下pydub安装配置、过程出现的问题及常用API >

    < python音频库:Windows下pydub安装配置.过程出现的问题及常用API > 背景 刚从B站上看过倒放挑战之后也想体验下,心血来潮一个晚上完成了基本的实现.其中倒放与播放部分 ...

  5. LeetCode37 使用回溯算法实现解数独,详解剪枝优化

    本文始发于个人公众号:TechFlow,原创不易,求个关注 数独是一个老少咸宜的益智游戏,一直有很多拥趸.但是有没有想过,数独游戏是怎么创造出来的呢?当然我们可以每一关都人工设置,但是显然这工作量非常 ...

  6. 用C++实现的解数独(Sudoku)程序

    我是一个C++初学者,控制台实现了一个解数独的小程序. 代码如下: //"数独游戏"V1.0 //李国良于2016年11月11日编写完成 #include <iostream ...

  7. python环境搭建-Linux系统下python2.6.6升级python3.5.2步骤

    [root@template ~]# python -v # /usr/lib64/python2.6/encodings/utf_8.pyc matches /usr/lib64/python2.6 ...

  8. 第四章 电商云化,4.1 17.5W秒级交易峰值下的混合云弹性架构之路(作者:唐三 乐竹 锐晟 潇谦)

    4.1 17.5W秒级交易峰值下的混合云弹性架构之路 前言 每年的双11都是一个全球狂欢的节日,随着每年交易逐年创造奇迹的背后,按照传统的方式,我们的成本也在逐年上升.双11当天的秒级交易峰值平时的近 ...

  9. Python+Django+Eclipse 在Windows下快速开发自己的网站

    一.配置开发环境 我的开发环境是:Python3.3.2 + Django1.5.2 + Eclipse 1.安装Python 下载地址:http://www.python.org/getit/ 安装 ...

随机推荐

  1. Django中间件加载原理

    假设我们有如下中间件: setting.py文件 MIDDLEWARE = [ 'django.middleware.A', 'django.middleware.B', 'django.middle ...

  2. 试题--创建三个进程/线程,依次输出 A、B、C

    这是一道机试题,大概的预期执行结果如下图所示 最近刚好在学习linux编程,便使用多线程及多进程分别实现了一遍,其中多线程较为简单,使用0/1信号量在线程间实现生产者/消费者即可:多进程则稍微复杂一些 ...

  3. 003-python基础-字符类型

    基本数据类型(int,bool,str) 1.基本数据数据类型: int 整数 str 字符串. 一般不存放大量的数据 bool 布尔值. 用来判断. True, False list 列表.用来存放 ...

  4. Gym 101257G:24(尺取)

    http://codeforces.com/gym/101257/problem/GGym 101257G 题意:给出n个人,和一个数s,接下来给出每个人当前的分数和输掉的概率.当一个人输了之后就会掉 ...

  5. 字符串匹配问题(暴力,kmp)

    对于字符串的匹配问题,现在自己能够掌握的就只有两种方法, 第一种就是我们常用的暴力匹配法,那什么是暴力匹配法呢? 假设我们现在有一个文本串和一个模式串,我们现在要找出模式串在文本串的哪个位置. 文本串 ...

  6. django基础知识之视图:

    视图 视图接受Web请求并且返回Web响应 视图就是一个python函数,被定义在views.py中 响应可以是一张网页的HTML内容,一个重定向,一个404错误等等 响应处理过程如下图:

  7. [AI开发]目标跟踪之计数

    基于视频结构化的应用中,目标在经过跟踪算法后,会得到一个唯一标识和它对应的运动轨迹,利用这两个数据我们可以做一些后续工作:测速(交通类应用场景).计数(交通类应用场景.安防类应用场景)以及行为检测(交 ...

  8. c++稍微复杂桶排序(未完待续~)

    由于上次的桶排序占用空间太多,这次又有了一个新的办法 直接上代码: #include <bits/stdc++.h> using namespace std; int n; void bu ...

  9. 華氏溫度轉化為攝氏溫度的簡單JavaScript代碼

    今天,跟著W3School學到了"JavaScript函數",代碼都挺簡單的,在運算符調用函數的地方寫了一個小程序.原碼程序是這樣的: <!DOCTYPE html> ...

  10. String到底在内存中是如何存储的

    String会出现在哪些地方 方法内的局部string 类内的字段String static string 容器中存储的string String数组 那么String的位置会影响其存储方式吗? 显然 ...