七桥问题python_Python系列—一笔画问题的算法研究
原标题:Python系列—一笔画问题的算法研究
鸣谢
VSRC感谢业界小伙伴——奶权,投稿精品原创类文章。VSRC欢迎精品原创类文章投稿,优秀文章一旦采纳发布,将有好礼相送,我们已为您准备好了丰富的奖品!
(活动最终解释权归VSRC所有)
0x00 一笔画完游戏
昨天和朋友出去外面吃饭,吃完饭后朋友打开了一个小程序玩了起来。
游戏长这样
大概玩法是:从地图中猫的位置开始出发,并且经过所有的格子就算过关。游戏还算挺有意思的,经过我的不断努力终于过到了30来关的样子。
并且随着游戏关卡的增加。游戏难度也变得越来越大,过一关需要非常久的时间。
最近也正好在研究算法,就打算看能不能写个通用的算法来找出每个地图的解。
0x01 哥尼斯堡的"七桥问题"
这个游戏的玩法和哥尼斯堡的"七桥问题"有点类似。
哥尼斯堡的"七桥问题":
18世纪著名古典数学问题之一。在哥尼斯堡的一个公园里,有七座桥将普雷格尔河中两个岛及岛与河岸连接起来(如图)。问是否可能从这四块陆 地中任一块出发,恰好通过每座桥一次,再回到起点?
当时人们想到的证明方法是把七座桥的走法都列出来一个一个试验,用排列组合的知识很容易得到七座桥所有的走法大概有7!=5040种,如果真的逐一试验,会是个很大的工作量。
但数学家欧拉没有这样想,欧拉把两座岛和河两岸抽象成顶点,七座桥抽象成连接每个顶点的七条边,那么这个问题就能被抽象成下面的图:
假设每座桥都恰好走过一次,那么对于A、B、C、D四个顶点中的每一个顶点,需要从某条边进入,同时从另一条边离开,进入和离开顶点的次数是相同的,即每个顶点有多少条进入的边,就有多少条出去的边。也就是说:每个顶点相连的边是成对出现的,即每个顶点的相连边的数量必须是偶数。
很明显,上图中A、C、D三个顶点相连的边都有3条,B顶点的相连边为5条,都是奇数。因此这个图无法从一个顶点出发,且恰好走过每座桥一次。
由此次证明人们又得到了欧拉路关系,要使得一个图形可以一笔画完,必须满足如下两个条件:
图形必须是连通的不能有孤立的点。
图中拥有奇数连接边的点必须是0或2。
对于一个连通图,通常把从某结点出发一笔画成所经过的路线叫做欧拉路。
那么这个游戏是不是就是让我们找到一条欧拉路呢?
0x02 对游戏进行抽象
按照上面证明七桥问题的方法,我们可以将游戏的地图抽象成这样:
其中14号顶点为起点
顶点和边的关系在程序中可以刻画成一个二维列表
graph = [
[1, 6], #0
[0, 2], #1
[1, 7, 3], #2
...
[24, 19] #25
]
graph列表的第一层表示每一个顶点,第二层则是与当前顶点有边的顶点,抽象完这张游戏地图后可以很清楚知道,这游戏并不是让我们找到一条欧拉路。
因为找到一条欧拉路,需要的是经过每一座桥,且只经过一次,也就是说每个顶点可以被多次经过。
而这个游戏需要的是经过每一个顶点,并不要求走完每一座桥,且顶点只能被经过一次。
0x03 哈密顿通路
在研究了七桥问题发现并不能解决这类问题后,我开始向团队的表哥们请教,其中一个表哥告诉我此类问题叫做哈密顿图(这里感谢下团队的**@xq17**表哥)。
这里说的哈密顿图,实际上是哈密顿通路的一种特殊情况 ,这种特殊情况指的是:
由指定的起点出发,途中经过所有其他顶点且只经过一次,最后返回起点,称之为哈密顿回路,如果给定的图G具有哈密顿回路,则称图G为哈密顿图。
那么现在目标明确了,这个游戏的解法就是找到某个给定图的哈密顿通路。
但是!!!
但是来了!!!
哈密顿通路问题,在上世纪七十年代初,被证明是NP-hard问题
一个复杂问题如果能在多项式时间内解决,那么它便被称为P类问题
一个复杂问题如果不能确定在多项式时间内解决,那么它便被称为NP类问题
什么意思呢?就拿质因数分解来说吧
P类问题: 23x37=?
NP类问题: 已知 axb=740914799,且a和b都是质数,求a和b的值 让我们来看看这个问题有多复杂:
因为 axb=740914799,且a和b都是质数
设 P={x|2<=x<740914799/2,x是质数}
易得 (a,b)∈PxP 即P与它自身的笛卡尔积
我们用一种叫做筛法的算法来求一下P集合的元素个数
一共有19841519个质数,算了我大概14分钟。
PxP的元素个数一共有19841519^2个,要一个个验证是否等于740914799,无疑又是一项很大的工程,这就是典型的NP类问题。NP类问题虽然难,但是可以很快验证一个给定的答案,是否正确。
比如上面的题,我告诉你答案a=22229 b=33331,你很快就能验证答案是否正确了。
而NP-hard问题则是比NP问题更难的问题,例如:围棋。
也就是说并不能找到一个友好的算法,来解决哈密顿通路问题。
0x04 算法设计
虽然找到一个图的哈密顿通路是NP困难的,但是好在游戏中的顶点不算太多,还是可以使用暴力一点的方法实现的,例如:图的深度优先遍历法(dfs)即递归和回溯法思想。
算法流程:
1. 将当前顶点压入已访问栈和路径栈中
将与当前顶点相通的顶点列出来
随机选取一个相通的顶点 并判断此顶点是否在已访问栈中
A.在已访问栈中则取另一个相通的顶点
B.不在则将这个相通的顶点作为当前顶点
C.若所有相通的顶点都在已访问栈中, 则判断路径栈是否包含所有顶点
a. 路径栈中包含所有顶点,则路径栈为当前图的哈密顿通路
b. 不包含所有顶点则回到父顶点, 并从已访问栈和路径栈中删除
反复执行1~3
0x05 算法实现
上面说过图的顶点和边的关系可以用一个二维列表来描述
graph = [
[1, 6],#0
[0, 2],#1
[1, 7, 3],#2
...
[24, 19]#25
]
但是要手动输入这些顶点和边的关系还是太麻烦了。仔细想了下,如果每个顶点的上下左右有顶点,那么就一定与上下左右的顶点有边,那么这个二维列表就可以简化成:
graph = [
[1,1,1,1,1,1],
[1,0,1,1,0,1],
[1,1,1,1,1,1],
[1,0,1,1,0,1],
[1,1,1,1,1,1],
[0,0,0,0,0,0] #每个1代表一个顶点 1与上下左右的1都有边 与0则没有 长宽相等易于编写代码
]
```
还可以再简化成一维列表:
graph = [
'111111',
'101101',
'111111',
'101101',
'111111',
'000000'
]
简直机智如我啊!
于是我写了个函数对一维列表进行转换
defget_index(i, j, G):
num = 0
forainxrange(i):
num += G[a].count('0')
forb inxrange(j):
ifG[i][b] == '0':
num += 1
returni * len(G) + j - num
defget_graph(G):
G = [list(x) forx inG]
EG = [ ]
fori inxrange(len(G)):
forj inrange(len(G[ i ])):
ifG[ i ][ j ] == '0':
continue
side_list = []
ifj+1<= len(G[i]) - 1:
ifG[i][j+1] == '1':
index = get_index(i, j-1, G)
side_list.append(index)
ifj-1>= 0:
ifG[i][j-1] == '1':
index = get_index(i, j-1, G)
side_list.append(index)
ifi+1<= len(G) - 1:
ifG[i+1][j] == '1':
index = get_index(i+1, j, G)
side_list.append(index)
ifi-1>= 0:
ifG[i-1][j] =='1':
index = get_index(i-1, j, G)
side_list.append(index)
EG.append(side_list)
returnEG
而算法的实现用图的邻接矩阵则更为方便,因此我写了一个将上列二位列表转换成邻接矩阵形式的函数:
defget_matrix(graph):
result = [[0]*len(graph)for _inxrange(len(graph))]# 初始化
fori inxrange(len(graph)):
forj ingraph[i]:
result[i][j] = 1# 有边则为1
returnresult
主要的dfs算法如下:
# graph为图的邻接矩阵 used为已访问栈 path为路径栈 step为已经遍历的顶点的个数
defdfs(graph, path, used, step):
ifstep == len(graph): # 判断顶点是否被遍历完毕
printpath
return True
else:
fori inxrange(len(graph)):
if notused[i] andgraph[path[step-1]][i] ==1:
used[i] = True
path[step] = i
ifdfs(graph, path, used, step+1):
return True
else:
used[i] = False# 回溯 返回父节点
path[step] = -1
return False
defmain(graph, v):
used = [ ] # 已访问栈
path = [ ] # 路径栈
fori inxrange(len(graph)):
used.append(False) # 初始化 所有顶点均未被遍历
path.append(-1) # 初始化 未选中起点及到达任何顶点
used[v] = True# 表示从起点开始遍历
path[0] = v # 表示哈密顿通路的第一个顶点为起点
dfs(graph, path, used, 1)
完整代码
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Coding with love by Naiquan.
defdfs(graph, path, used, step):
ifstep == len(graph):
printpath
return True
else:
foriinxrange(len(graph)):
if notused[i] andgraph[path[step-1]][i] == 1:
used[i] =True
path[step] = i
ifdfs(graph, path, used, step+1):
return True
else:
used[i] = False
path[step] = -1
return False
defmain(graph, v):
used = [ ]
path = [ ]
fori inxrange(len(graph)):
used.append(False)
path.append(-1)
used[v] =True
path[0] = v
dfs(graph, path, used, 1)
defget_index(i, j, G):
num = 0
fora inxrange(i):
num += G[a].count('0')
forb inxrange(j):
ifG[i][b] =='0':
num += 1
returni * len(G) + j - num
defget_graph(G):
G = [list(x) forx inG]
EG = []
fori inxrange(len(G)):
forj inrange(len(G[i])):
ifG[i][j] == '0':
continue
side_list = []
ifj+1<= len(G[i]) - 1:
ifG[i][j+1] == '1':
index = get_index(i, j+1, G)
side_list.append(index)
ifj-1>= 0:
ifG[i][j-1] == '1':
index = get_index(i, j-1, G)
side_list.append(index)
ifi+1<= len(G) -1:
ifG[i+1][j] == '1':
index = get_index(i+1, j, G)
side_list.append(index)
ifi-1>=0:
ifG[i-1][j] == '1':
index = get_index(i-1, j, G)
side_list.append(index)
EG.append(side_list)
returnEG
defget_matrix(graph):
result = [[0]*len(graph) for_ inxrange(len(graph))]
fori inxrange(len(graph)):
forj ingraph[i]:
result[i][j] =1
returnresult
if__name__ == '__main__':
map_list = [
'111111',
'101101',
'111111',
'101101',
'111111',
'000000'
]
G = get_graph(map_list)
map_matrix = get_matrix(G)
# print map_matrix
SP = 14
main(map_matrix, SP)
0x06 结束
在实现了功能后,我拿着这个程序成功过到了差不多一百关,然后就玩腻了,哈哈哈哈哈哈哈哈哈
0x07 本文参考资料
1. 七桥问题_百度百科
【https://baike.baidu.com/item/七桥问题/2580504?fr=aladdin】
2. 哈密顿图_百度百科
【https://baike.baidu.com/item/哈密顿图/2587317?fr=aladdin】
3. 这个数学题我做可以,但世界毁灭了别怪我【https://www.bilibili.com/video/av19009866】
4. 基于回溯法寻找哈密顿回路
【http://www.cnblogs.com/cielosun/p/5654577.html】返回搜狐,查看更多
责任编辑:
七桥问题python_Python系列—一笔画问题的算法研究相关推荐
- python生成一笔画_Python系列—一笔画问题的算法研究
原标题:Python系列-一笔画问题的算法研究 鸣谢 VSRC感谢业界小伙伴--奶权,投稿精品原创类文章.VSRC欢迎精品原创类文章投稿,优秀文章一旦采纳发布,将有好礼相送,我们已为您准备好了丰富的奖 ...
- 七桥问题c语言程序数据结构,数据结构与算法学习——图论
什么是图? 在计算机程序设计中,图结构也是一种非常常见的数据结构 但是图论其实是一个非常大的话题 图结构是一种与树结构有些相似的数据结构 图论是数学的一个分支,并且在数学概念上,树是图的一种 它以图为 ...
- 图论的起源:柯尼斯堡七桥(一笔画)问题与欧拉路径/回路
柯尼斯堡七桥问题 大数学家欧拉一生中的大部分时间在俄国和普鲁士度过.1735年,他提出了著名的柯尼斯堡七桥(Seven Bridges of Königsberg)问题: 柯尼斯堡(今俄罗斯加里宁格勒 ...
- 经典算法研究系列:九、图像特征提取与匹配之SIFT算法
经典算法研究系列:九.SIFT算法研究 作者:July.二零一一年二月十五日. 推荐阅读: David G. Lowe, "Distinctive image features from s ...
- 七桥问题python_用Python玩烧脑小游戏《一笔画完》,瞬间闯到100关
Python 的概念 昨天和朋友出去外面吃饭,吃完饭后朋友打开了一个小程序玩了起来...... 加小编QQ群:832339352即可自动获取大量Python视频教程以及各类PDF! 游戏长这样 大概玩 ...
- 哥尼斯堡七桥问题用计算机,哥尼斯堡七桥问题解法真的解不出来?请尽快解答.急...
十八世纪,东普鲁士的首府哥尼斯堡是一座景色迷人的城市,普莱格尔河横贯城区,使这 座城市锦上添花,显得更加风光旖旋.这条河有两条支流,在城中心汇成大河,在河的 中央有一座美丽的小岛.河上有七座各具特色的 ...
- 欧拉如何解决哥尼斯堡七桥问题(二)
上一讲欧拉已经证明哥尼斯堡七桥问题不存在每座桥只走一次的走法.但是这里有一个特殊情况,即A.B.C.D每个地区恰巧都有奇数个桥连通.欧拉当然也想到了如果存在偶数桥,及任意数量的河和桥图是否存在每座桥只 ...
- 世界数学难题——哥尼斯堡七桥问题 哥尼斯堡七桥问题
七桥问题 七桥问题Seven Bridges Problem 18世纪著名古典数学问题之一.在哥尼斯堡的一个公园里,有七座桥将普雷格尔河中两个岛及岛与河岸连接起来(如图).问是否可能从这四块陆地中任一 ...
- 给有兴趣的人-图论趣谈(七桥问题 )
因为个人原因,停更了好久,孱弱弱来补坑啦,这次分享的是有趣的问题--七桥问题和环游世界问题,两个问题十分著名,分别七桥问题-欧拉,环游世界-哈密顿这两位图论界的大拿提出的,其中七桥问题被称之为图论的的 ...
最新文章
- Java集合7 (NavigableSet)
- 关于面试题的一些题型解析
- Visual Studio调试之断点技巧篇
- 杨森翔的书法【斗方】
- android 悬浮组件,Android 悬浮组件
- WindowsAPI中W和A的区别
- linux-gcc 编译时头文件和库文件搜索路径
- Python max函数中key的用法
- sqlserver 修改表字段长度
- echarts官网demo
- 泰迪杯数据挖掘挑战赛—机器学习(一)
- 小工具 | 横断面面积及土方量计算excel表格
- Excel如何删除空白行
- 从通信的角度理解现场总线
- 技术停滞:如何更新?
- 局域网传输文件详解(转)
- PTA-C语言-解一元二次方程
- 如何将Word中数据转换为excel表格
- Linux运维笔记-日常操作命令总结(1)
- 天使跟我走,世界我都有
热门文章
- 线上展厅展台搭建方法汇总
- NVIDIA驱动失效
- photoswipe-3.0.5 手机端横屏后竖屏图片无法归位问题解决
- InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is s
- 【R的网络提取】CSDN博客列表和url的提取
- sql查询合并多个字符串
- mdadm删除raid_软RAID管理命令mdadm详解 | 旺旺知识库
- 推荐一个C#操作SVG图形矢量图的开源项目
- 优化ApK大小之ABI Filters 和 APK split
- 在SharePoint管理中心管理SharePoint Designer设置