本题难度系数为35%(最易5%,最难100%)。
一只蚂蚁在涂有黑色或白色的普通方格上移动。蚂蚁总是朝向四种基本方向(左、右、上、下)之一,每次遵循以下规则移动到相邻的方格:
- 如果它在黑色方格上,它将方格的颜色变为白色,逆时针旋转90度,向前移动一个方格
- 如果它在白色方格上,它将方格的颜色变为黑色,顺时针旋转90度,向前移动一个方格
从全白的网格开始,在蚂蚁移动 1 0 18 10^{18} 1018次之后,有多少个方格是黑色的?
解:
小海龟模拟画图
用小海龟模拟蚂蚁的行为。
import turtle as tt
from collections import defaultdict
tt.screensize(bg="green")
tt.speed(9)
tt.left(90) # 让小海龟朝正北方向
tt.penup()
x, y = 0, 0
dx, dy = 0, 1
colors = defaultdict(int)
while True:
if colors[(x, y)]:
tt.left(90)
dx, dy = -dy, dx
else:
tt.right(90)
dx, dy = dy, -dx
colors[(x, y)] = 1 - colors[(x, y)]
x, y = x+dx, y+dy
if colors[(x, y)]:
tt.color('green')
else:
tt.color('black')
tt.write('■', align='center', font=("Consolas", 10, "normal"))
tt.forward(10)
一开始看不出明显的规律,一直走了1万多步之后,它进入到一种无限循环的模式。
录了一小段动画,看一下效果。
网上也有人写过类似的代码,实现方式与我的不一样。
# https://www.geeksforgeeks.org/python-langtons-ant/
import turtle
def langton():
window = turtle.Screen()
window.bgcolor('white')
window.screensize(1000, 1000)
maps = {}
ant = turtle.Turtle()
ant.left(90)
ant.shape('square')
ant.shapesize(0.5)
ant.speed(999)
pos = coordinate(ant)
while True:
step = 10
if pos not in maps or maps[pos] == "white":
ant.fillcolor("black")
ant.stamp()
invert(maps, ant, "black")
ant.right(90)
ant.forward(step)
pos = coordinate(ant)
elif maps[pos] == "black":
ant.fillcolor("white")
invert(maps, ant, "white")
ant.stamp()
ant.left(90)
ant.forward(step)
pos = coordinate(ant)
def invert(graph, ant, color):
graph[coordinate(ant)] = color
def coordinate(ant):
return (round(ant.xcor()), round(ant.ycor()))
langton()
确定重复模式
小海龟画图太慢,用matplotlib生成多幅图片,寻找规律。为了让图像向第一象限生长,初始小蚂蚁的方向朝南。
import matplotlib.pyplot as plt
import numpy as np
def black_squares(steps):
x, y = 0, 0
dx, dy = 0, -1 # 一开始蚂蚁朝南,这样图形会向第一象限无限循环
blacks = set()
for i in range(1, steps+1):
if (x, y) in blacks:
blacks.remove((x, y))
dx, dy = -dy, dx
else:
blacks.add((x, y))
dx, dy = dy, -dx
x, y = x + dx, y + dy
return blacks
def langton_pic(steps):
b0 = black_squares(steps)
fig, ax = plt.subplots()
xs = [p[0] for p in b0]
ys = [p[1] for p in b0]
ax.plot(xs, ys, 's', markersize=3, markerfacecolor='black',
markeredgecolor='green', markeredgewidth=0.1)
ax.set_aspect('equal')
ax.axhline(color='green', linewidth=0.6, linestyle='-') # 水平轴
ax.axvline(color='green', lw=0.6, ls='-')
ax.set_xlim(-30, 80)
ax.set_ylim(-30, 50)
ax.set_title(f"langton's ant {steps}")
ax.text(45, 45, f'black squares: {len(b0)}')
plt.savefig(f'd://langton_ant/{steps}.png', dpi=200)
plt.close()
for step in range(10000, 11000, 50):
langton_pic(step)
仔细分析几幅图片,可以发现大概10500步之后,每104步,右边的茶壶嘴“长”了一小截。黑色方块多出12块。
现在可以计算出正确答案了,这里先随便找了一个大于10500的整数:12000,计算10**18距离12000要重复的次数,再计算出最初的步数,累加即可。
repeat = (10**18 - 12000) // 104
start = 10**18 - repeat * 104
print(len(black_squares(start)) + repeat * 12)
其它参考
网上关于这个问题的资料非常多,列出几个:
https://en.wikipedia.org/wiki/Langton%27s_ant
http://www.langtonant.com/
https://josephpetitti.com/ant
https://golly.sourceforge.net/webapp/golly.html
https://en.wikipedia.org/wiki/Golly_(program)
https://en.wikipedia.org/wiki/Hashlife
https://pypi.org/project/python-lifelib/2.0.9/