本文只讨论单链情况,无分支
总体思路:
1.以第一个节点为原点(0,0,0),依次向外生长,0、1、2、3、4、5分别代表6个生长方向(0:x+1;1:x-1; 2:y+1; 3:y-1; 4:z+1; 5:z-1)。例如列表[0,2,1,1,4],表示x+1,y+1,x-1,x-1,z+1
2.itertools.product遍历所有情况,然后删去旋转对称的、镜像对称的、节点顺序反向的(二维情况更为简单,无需旋转镜像)
3.再删去有含重叠节点的情况(即自身与自身交叉的情况)
4.把相对位置转换为绝对坐标,画图
定义旋转和镜像(对列表内容进行改变)
import itertools
from matplotlib import pyplot as plt
from matplotlib.pyplot import MultipleLocator
import os
#镜像,分别有三种角度的镜像
def mirror(l1,axis=0):
l = l1.copy()
if axis==0:
for i in range(len(l)):
if l[i] == 0:
l[i] = 1
elif l[i] == 1:
l[i] = 0
elif axis==1:
for i in range(len(l)):
if l[i] == 2:
l[i] = 3
elif l[i] == 3:
l[i] = 2
elif axis==2:
for i in range(len(l)):
if l[i] == 4:
l[i] = 5
elif l[i] == 5:
l[i] = 4
return l
#旋转,有三个方向的旋转,可以旋转0~3度,1度代表90°
def rotate(l1,axis=0,degree=1):
l=l1.copy()
if degree==0:
return l
if axis == 0:
for i in range(len(l)):
if degree==1:
if l[i]==0:
l[i]=3
elif l[i]==1:
l[i]=2
elif l[i] == 2:
l[i] = 0
elif l[i] == 3:
l[i] = 1
elif degree==2:
if l[i]==0:
l[i]=1
elif l[i]==1:
l[i]=0
elif l[i] == 2:
l[i] = 3
elif l[i] == 3:
l[i] = 2
elif degree==3:
if l[i]==0:
l[i]=2
elif l[i]==1:
l[i]=3
elif l[i] == 2:
l[i] = 1
elif l[i] == 3:
l[i] = 0
elif axis == 1:
for i in range(len(l)):
if degree==1:
if l[i]==0:
l[i]=5
elif l[i]==1:
l[i]=4
elif l[i] == 4:
l[i] = 0
elif l[i] == 5:
l[i] = 1
elif degree==2:
if l[i]==0:
l[i]=1
elif l[i]==1:
l[i]=0
elif l[i] == 4:
l[i] = 5
elif l[i] == 5:
l[i] = 4
elif degree==3:
if l[i]==0:
l[i]=4
elif l[i]==1:
l[i]=5
elif l[i] == 4:
l[i] = 1
elif l[i] == 5:
l[i] = 0
elif axis == 2:
for i in range(len(l)):
if degree==1:
if l[i]==4:
l[i]=3
elif l[i]==5:
l[i]=2
elif l[i] == 2:
l[i] = 4
elif l[i] == 3:
l[i] = 5
elif degree==2:
if l[i]==4:
l[i]=5
elif l[i]==5:
l[i]=4
elif l[i] == 2:
l[i] = 3
elif l[i] == 3:
l[i] = 2
elif degree==3:
if l[i]==4:
l[i]=2
elif l[i]==5:
l[i]=3
elif l[i] == 2:
l[i] = 5
elif l[i] == 3:
l[i] = 4
return l
删去重复情况
对每种情况进行三维度的旋转和镜像,每种情况通过旋转、镜像、反序的组合一共可产生4 * 4 * 4 * 2 * 2 * 2 * 2=1024种情况(包括自身),相当于每种情况都有1024种重复情况,但是旋转2度相当于两重镜像,旋转3度相当于旋转一度+镜像,所以不需要考虑旋转2或3度的情况,所以有2 * 2 * 2 * 2 * 2 * 2 * 2=128种情况。每做出一种变换,就在总的集合内删去此变换出来的情况,但要保证不删去自己。
def del_rotate_3D(l1_all):
l=l1_all.copy()
l2=l.copy()
i=0
while i<len(l):
for degree1 in range(2):
for degree2 in range(2):
for degree3 in range(2):
for m1 in range(2):
for m2 in range(2):
for m3 in range(2):
l2[i]=rotate(l[i],0,degree1)
l2[i] = rotate(l2[i], 1, degree2)
l2[i] = rotate(l2[i], 2, degree3)
if m1==1:l2[i]=mirror(l2[i],0)
if m2==1:l2[i] = mirror(l2[i], 1)
if m3==1:l2[i] = mirror(l2[i], 2)
if l2[i]==l[i]:
pass
else:
try:l.remove(l2[i])
except:pass
l2[i].reverse()
if l2[i] == l[i]:
pass
else:
try:l.remove(l2[i])
except:pass
i+=1
return l
输入每种情况的列表组成的列表,把相对位置转换为绝对坐标,若有重复坐标则删除这种情况
def delete_cross_3D(chosen_conf):
list4 = []
for ii in range(len(chosen_conf)):
xl = [0]
yl = [0]
zl=[0]
for s in chosen_conf[ii]:
if s == 0:
xl.append(xl[-1]+1)
yl.append(yl[-1])
zl.append(zl[-1])
elif s == 1:
xl.append(xl[-1]-1)
yl.append(yl[-1])
zl.append(zl[-1])
elif s == 2:
xl.append(xl[-1])
yl.append(yl[-1] + 1)
zl.append(zl[-1])
elif s == 3:
xl.append(xl[-1])
yl.append(yl[-1]-1)
zl.append(zl[-1])
elif s == 4:
xl.append(xl[-1])
yl.append(yl[-1])
zl.append(zl[-1]+1)
elif s == 5:
xl.append(xl[-1])
yl.append(yl[-1])
zl.append(zl[-1]-1)
xyz = list(zip(xl, yl,zl))
if len(set(xyz)) == len(xyz):
list4.append(chosen_conf[ii])
else:
pass
return list4
输入一个列表,把相对位置转换为绝对坐标,返回x,y,z
def relative2absolute(l1):
l=l1.copy()
xl = [0]
yl = [0]
zl = [0]
for s in l:
if s == 0:
xl.append(xl[-1] - 1)
yl.append(yl[-1])
zl.append(zl[-1])
elif s == 1:
xl.append(xl[-1] + 1)
yl.append(yl[-1])
zl.append(zl[-1])
elif s == 2:
xl.append(xl[-1])
yl.append(yl[-1] - 1)
zl.append(zl[-1])
elif s == 3:
xl.append(xl[-1])
yl.append(yl[-1] + 1)
zl.append(zl[-1])
elif s == 4:
xl.append(xl[-1])
yl.append(yl[-1])
zl.append(zl[-1] - 1)
elif s == 5:
xl.append(xl[-1])
yl.append(yl[-1])
zl.append(zl[-1] + 1)
return xl,yl,zl
画图,ax.plot3D
def draw_figures(xm,ym,zm,seq_num):
n=len(xm)
x_major_locator = MultipleLocator(1)
# 把x轴的刻度间隔设置为1,并存在变量里
y_major_locator = MultipleLocator(1)
z_major_locator = MultipleLocator(1)
# 把y轴的刻度间隔设置为10,并存在变量里
ax = plt.axes(projection='3d')
# ax为两条坐标轴的实例
ax.xaxis.set_major_locator(x_major_locator)
# 把x轴的主刻度设置为1的倍数
ax.yaxis.set_major_locator(y_major_locator)
ax.zaxis.set_major_locator(z_major_locator)
xmin = min(xm+ym+zm)
xmax = max(xm+ym+zm)
xdif=xmax-xmin
#plt.figure(iii)
ax.plot3D(xm,ym,zm, 'ro-')
ax.set_xlim(min(xm), min(xm)+xdif + 0.5)
ax.set_ylim(min(ym), min(ym)+xdif + 0.5)
ax.set_zlim(min(zm), min(zm)+xdif + 0.5)
plt.savefig('n='+str(n)+'_3D/' + str("{:0>5d}".format(seq_num)) + ".png")
plt.clf()
#plt.show()
# plt.close('all')
总体过程
def generate3(n):
conf=list(itertools.product([0,1,2,3,4,5], repeat=n-1))#生成所有情况
for i in range(len(conf)):
conf[i]=list(conf[i])
a=delete_cross_3D(conf)
a = del_rotate_3D(a)
if not os.path.exists('n='+str(n)+'_3D'):
os.mkdir('n='+str(n)+'_3D')
seq_num=0
for con in a:
seq_num+=1
xld,yld,zld=relative2absolute(con)
draw_figures(xld,yld,zld,seq_num)
print(len(a))
return a
调用函数,生成所有情况的图片。设有4个节点,n=4
x=generate3(4)
结果有5种情况:
0 0 0
0 0 2
0 2 0
0 2 1
0 2 4
024
002
000
020
021
如果是二维情况,无需旋转镜像,用此方法即可
import itertools
from itertools import combinations,permutations
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.pyplot import MultipleLocator
import pickle
import os
#在已有数据基础上增加一个格点,头尾各有4种情况,一种情况变为八种
def add_one_residue(front_conf_list):
conf_add = list(itertools.product([0, 1, 2, 3], repeat=1))
added_conf_list = []
for ii in range(len(front_conf_list)):
for i in range(len(conf_add)):
added_conf_list.append(front_conf_list[ii] + list(conf_add[i]))
added_conf_list.append(list(conf_add[i])+front_conf_list[ii])
return added_conf_list
#相对转绝对坐标
def relative2absolute(conf_l):
listx = []
listy=[]
for ii in range(len(conf_l)):
xl = [0]
yl = [0]
for s in conf_l[ii]:
if s == 0:
xl.append(xl[-1]+1)
yl.append(yl[-1] )
elif s == 1:
xl.append(xl[-1] - 1)
yl.append(yl[-1])
elif s == 2:
xl.append(xl[-1])
yl.append(yl[-1] + 1)
elif s == 3:
xl.append(xl[-1] )
yl.append(yl[-1]- 1)
listx.append(xl)
listy.append(yl)
return listx,listy
def draw_figures(axis_list1,axis_list2,n):
iii = 0
x_major_locator = MultipleLocator(1)
# 把x轴的刻度间隔设置为1,并存在变量里
y_major_locator = MultipleLocator(1)
# 把y轴的刻度间隔设置为10,并存在变量里
for i in range(len(axis_list1)):
ax = plt.gca()
# ax为两条坐标轴的实例
ax.xaxis.set_major_locator(x_major_locator)
# 把x轴的主刻度设置为1的倍数
ax.yaxis.set_major_locator(y_major_locator)
xl=axis_list1[i]
yl=axis_list2[i]
xmin = min(xl + yl)
xmax = max(xl + yl)
xdif=xmax-xmin
iii += 1
#plt.figure(iii)
plt.plot(xl, yl, 'ro-')
plt.xlim(min(xl) - 0.5, min(xl)+xdif + 0.5)
plt.ylim(min(yl) - 0.5, min(yl)+xdif + 0.5)
plt.savefig('n='+str(n)+'_2D/' + str("{:0>5d}".format(iii)) + ".png")
plt.clf()
#plt.show()
# plt.close('all')
#列表中不论什么数字,按出现顺序标为0,1,2,3,例如[3,1,1,2,3,0,0] 变为[0,1,1,2,0,3,3].
# 一个列表不论旋转或镜像,按照此变换后都一样。这种形式称为字母型
def num2letter(l1):
l=l1.copy()
o=[]
re=[]
for i in range(len(l)):
if not (l[i] in re):
re.append(l[i])
for i in range(len(l)):
o.append(re.index(l[i]))
return o
#是否有重叠点
def if_cross(l1):
l=l1.copy()
xl = [0]
yl = [0]
zl = [0]
for s in l:
if s == 0:
xl.append(xl[-1] - 1)
yl.append(yl[-1])
zl.append(zl[-1])
elif s == 1:
xl.append(xl[-1] + 1)
yl.append(yl[-1])
zl.append(zl[-1])
elif s == 2:
xl.append(xl[-1])
yl.append(yl[-1] - 1)
zl.append(zl[-1])
elif s == 3:
xl.append(xl[-1])
yl.append(yl[-1] + 1)
zl.append(zl[-1])
elif s == 4:
xl.append(xl[-1])
yl.append(yl[-1])
zl.append(zl[-1] - 1)
elif s == 5:
xl.append(xl[-1])
yl.append(yl[-1])
zl.append(zl[-1] + 1)
xyz = list(zip(xl, yl, zl))
if len(set(xyz)) == len(xyz):
return True
else:return False
def choose_unrepeat2(l1):
l=l1.copy()
l2=l1.copy()
recl=[]#记录不同字母型
lout=[]#记录输出
for i in range(len(l)):
if if_cross(l[i]) == False: continue
letter_type=num2letter(l[i]) #转换为字母型
l2[i].reverse()
letter_type2 = num2letter(l2[i])#反序后再转换为字母型
if letter_type in recl or letter_type2 in recl:
pass#如果此种字母型已有,代表是重复情况
else:
recl.append(letter_type)
lout.append(l[i])
return lout
def generate2(n):
conf=list(itertools.product([0,1,2,3], repeat=2))#从n=3的简单情况开始
for i in range(len(conf)):
conf[i]=list(conf[i])#元组转换为列表
a=conf
for ii in range(3,n):
a=add_one_residue(a)#增加一个点
a=choose_unrepeat2(a)#再筛选
print(len(a))
if not os.path.exists('n='+str(n)+'_2D'):
os.mkdir('n='+str(n)+'_2D')
print('drawing')
axx,axy=relative2absolute(a)
draw_figures(axx,axy,n)
output = open('n='+str(n)+'_2D_'+'data.pkl', 'wb') #保存数据
pickle.dump(a, output)
return a
x=generate2(4)#简便方法
这些情况之间存在一步转换关系(即两条链只有一个点坐标不同),可画出其邻接矩阵,见下一篇