単純なルールでself-avoiding path

  • 先日のMIKUでフラーレン上のエッジを選び方が話題になった
  • あるルールでエッジを選んでいくと、通り道の途中にぶつかることなく、出発点に戻る(サイクルを作る)ことが必然であることが示された
  • フラーレンは閉じた空間・有限な空間なので、いつかは通り道のどこかにぶつかるしかなく、「途中にぶつかることを禁止」してエッジを選ぶと結果として出発点に戻るしかない、という論理だった
  • フラーレンは六角形と五角形とが規則正しく配置して、結果として球面に並ぶ構造で、サッカーボールと同じ

  • 展開図にすると

  • サッカーボールは3次元で閉じてしまうので、平面にして(無限にもなるのだが)みる。展開図の「黒い五角形」を「(黒い)六角形」にすると、ベンゼン環を敷き詰めた構造と同じになる。しかも、「白い六角形」は3つの二重結合を持つ環で、「黒い六角形」は「穴」であってベンゼン環ではない(これはグラフェン(Wiki記事)

http://www.org-chem.org/yuuki/planar/graphene.jpg(http://www.org-chem.org/yuuki/planar/planar.htmlより)

  • グラフェン上をごく単純なルールで酔歩すると、どん詰まりにならずにself-avoiding pathができる。self-avoiding pathは空間探索のやりかたとして餌探しのときなどには有効(なはず)なので、ちょっと面白い

  • 以下、ソース(これをサッカーボール状に丸めたい…→こちら)
# 酔歩の歩数
#n.iter <- 10000 # 掲載図は10000歩
n.iter <- 1000
# 酔歩の座標格納行列
X <- matrix(0,n.iter,2)
# 酔歩の1歩は1重結合か2重結合かの2種類なので、それを記録
# ただし、ルールとして、1重、2重が交互になるように歩く
e.type <- rep(0,n.iter-1)
# 1歩のベクトルは6種類ある。その6種類の履歴を格納
e.vector.id <- rep(0,n.iter-1)
# 6種類のベクトルを格納するvs
t <- (seq(from=0,to=1,length=7)*2*pi)[-7]
vs <- cbind(cos(t),sin(t))
# ある一歩の次の一歩は、最後の一歩から左右に30度の2方向のうちのどちらかになる。その最後の一歩と選べる2ベクトルの関係のベクトル
vs.relation <- matrix(0,6,6)
for(i in 1:6){
	for(j in 1:6){
		if(abs(i-j)==1){
			vs.relation[i,j] <- 1
		}
	}
}
vs.relation[1,6] <- vs.relation[6,1] <- 1
vs.relation
# 最初の2歩は決め打ちにする(決め打ちにしても一般性は(ほぼ)失われない)
e.type[1] <- 1
e.type[2] <- 2
e.vector.id[1] <- 1
e.vector.id[2] <- 2
X[2,] <- X[1,] + vs[e.vector.id[1],]
X[3,] <- X[2,] + vs[e.vector.id[2],]
# 3歩目と3歩目が決める4番目の位置から、n.iter番目の位置までを酔歩する
for(i in 4:n.iter){
# 来た時が1重結合だったら、次は2重結合
	if(e.type[i-2]==1){
# 選びうる2つのベクトルのうち、片方は1重結合でもう片方は2重結合になっている(グラフェンの性質から)
# 選べる2ベクトルのうち、1重結合なのは、ひとつ前の1歩のベクトル(それは2重結合だった)と同じベクトルなので、それとは違う方を選ばせる
# つまり、選択の余地はなく、ルールに従ってかならずある方向に進む
		to.select <- which(vs.relation[e.vector.id[i-2],]==1)
		e.type[i-1] <- 2
		e.vector.id[i-1] <- to.select[which(to.select!=e.vector.id[i-3])]
		X[i,] <- X[i-1,] + vs[e.vector.id[i-1],]
# 来た時が2重結合だったら、次は1重結合
	}else{
# 1重結合が必ず2本ある
# 2本のうち、どちらかをランダムに選ぶ
# ただし、もし、選んで1歩進んだ先がすでに通った道の上の点ならそれを選ぶのはやめる
# また、選んだ1歩(1重結合)の先の2重結合は決まっているのだから、2歩先も読めて、2歩先がすでに通った道の上の点なら、やはり選ぶのをやめる
# 選ぶのをやめたら、もう片方を選ぶしかない
# 次の1歩の候補ベクトルIDをランダムに選ぶ
		to.select <- sample(which(vs.relation[e.vector.id[i-2],]==1))
		e.type[i-1] <- 1
		tmp.e1 <- to.select[1]
		to.select.2.1 <- which(vs.relation[tmp.e1,]==1)
		tmp.e1.2 <- to.select.2.1[which(to.select.2.1!=e.vector.id[i-2])]
# 1歩目、2歩目の座標を仮に計算する
		tmp.X1 <- X[i-1,] + vs[tmp.e1,]
		tmp.X1.2 <- tmp.X1 + vs[tmp.e1.2,]
# 1歩目、2歩目の座標と、これまでに通った道の上の点(ただし、出発点を除く)との距離を計算する
		dist1 <- apply((t(t(X[2:(i-1),])-tmp.X1))^2,1,sum)
		dist2 <- apply((t(t(X[2:(i-1),])-tmp.X1.2))^2,1,sum)
# もし同じ点の上に来たら、選択をやめる(ただし、距離が0かどうかで判定すると、計算の誤差でうまく行かないので不等号を使ってある
		if(min(c(dist1,dist2)) > 10^(-4)){
		#if(min(c(dist1,dist2)) != 10^(-4)){
			e.vector.id[i-1] <- to.select[1]
			X[i,] <- tmp.X1
		}else{
			e.vector.id[i-1] <- to.select[2]
			X[i, ] <- X[i-1,] + vs[e.vector.id[i-1],]
		}

	}
# もし、出発点に戻ったなら、処理をやめる
	if(sum((X[1,]-X[i,])^2)==0){
		break;
	}
}
# プロットする
# 1重結合は黒、2重結合は赤
xlim <- ylim <- range(X)
plot(X,cex = 100/n.iter,xlim = xlim,ylim=ylim)
for(i in 1:length(e.type)){
	segments(X[i,1],X[i,2],X[i+1,1],X[i+1,2],col=e.type[i])
}