自回避随机行走问题(SAW),即在格点上不与历史轨迹相撞的随机行走,本文简要说明一种利用python模拟和绘制SAW路径的方法。(完整代码在文章末尾,文字部分是按思考的顺序,代码是最终版本,中间部分代码会出现和文字有出入的内容,建议先看思路)
该路径生成碰到的主要问题是锁死,原因在于路径闭合后随机走向了空间有限的一侧,导致在达到设定步数前出现无路可走的情况。该方法的核心就是在路径闭合时选择闭合圈的外侧,避免进入圈内锁死。
为简单起见所用的是标准的方形网格,可能的前进方向只有4种,排除上一步的落点对应方向,除第一步外(第一步四向随机)可能前进的方向至多三种。设定一个每步更新的可行方向集合,接下来的限制条件均是从中去除某些项。
(Tips:方向可设为ev=[(1,0),(0,1),(-1,0),(0,-1)],行进用坐标相加,某方向的取反%4,向右(-1)%4,向左(+1)%4,后面会用上)
ev=[(1,0),(0,1),(-1,0),(0,-1)]
为了避免判断历史轨迹需要遍历,将网格赋值为零矩阵,每一步的落点赋值为1,仅需判断周围格点的值,不为0可把对应方向排除。边界设为-1,避免和路径混淆。
order=np.zeros((2*s+3,2*s+3),dtype=int) #顺序矩阵
direc=[] #方向矩阵
for i in np.arange(2*s+3): #设定边界为-1
order[0][i]=-1
order[-1][i]=-1
order[i][0]=-1
order[i][-1]=-1
nucx=[s+1] #路径位置记录
nucy=[s+1]
new=(s+1,s+1) #暂时位置
order[s+1][s+1]=1 #设定起点
经简单推导可知,仅路径前端的前、左、右,斜前方5格内出现1才会出现锁死的可能,故就此进行分类讨论。(示意图如下,红色为路径,黄色为讨论区块)
垂直方向(五角星处)优先判断,出现一个1记一个“wall”(每步清零),且在可行方向集合中除去该方向。
wall=0
avi=[0,1,2,3]
ju=0
if k!=0: #第一步完全随机
avi.remove((direc[-1]+2)%4)
for i in np.arange(3):
ju=order[new[0]+ev[(direc[-1]-1+i)%4][0]][new[1]+ev[(direc[-1]-1+i)%4][1]]
if ju!=0:
wall+=1
avi.remove((direc[-1]-1+i)%4)
wall==3即为锁死,可输出锁死时的步数和位置,输出图进行比对,检查出现锁死的原因。(在前期用于修正和添加限制条件,认图找端点困难时步数也一般很大,出现锁死的几率不大,算法接近完整后可换成归零条件,即重新计算至能跑到规定步数为止)
if wall==3:
#print(k)
#print(new)
restart=True
break
wall==2即只有一个方向可走,下一步方向唯一,无需再做排除。
wall==1即有一个方向被堵,此时分为两种情况:前方被堵和侧面被堵。其中前方被堵必定出现了路径闭合,只能选择一个方向。经推导易知在闭合圈不包围原点时,应排除与"wall"所在格前一步或后一步相同的方向(在可行方向内);包围原点时,应排除相反的方向。
此时发现原本的数值矩阵不能用于该判断,再引入一个方向集合和顺序矩阵,顺序矩阵可以代替数值矩阵。方向集合记录每一步 的方向(ev集合的序号),顺序矩阵初始也是零矩阵,每步的落点赋值该步的顺序。判断某点前后的方向只需以该点顺序作为索引调取方向集合的值。
判断闭合圈是否包围原点则从发生闭合的端点开始(此处是从"wall"所在格开始),沿路径检查其经过象限。新增一个判断集合,若新元素和集合内已有的倒数第二个元素相同(如出现1 4 1),则该元素和倒数第一个元素都可弃置(对是否围住原点没有影响