我最近创建了一个脚本来创建Dragon Curve,并设法优化代码。
基本上,我首先生成一个规则列表,看起来像[1, 1, -1, 1, 1, -1, -1]
,其中1代表右转,-1代表左转。 numpy数组的速度非常快。
有关龙曲线的更多信息:http://en.wikipedia.org/wiki/Dragon_curve
但是,现在我想使用此列表在平面中创建一条曲线。基本上:选择一个点(x,y)和一个方向(东),走一步,然后向右或向左转90度,具体取决于列表中的当前元素,我们将其循环。我们最后还要采取额外措施,但这不应该与问题有关。
让我们说我们的起始位置是(100,100),我们开始往东走,列表是[1, 1, -1]
。然后我们应该得到[(100, 100), (101, 100), (101, 101), (101, 101), (100, 101), (100, 102)]
,这给了我们龙曲线的第二次迭代。
目前,我使用以下代码生成点序列:
pos = [100, 100]
ang = math.pi/2
for i in dragon + [0]:
pos.extend([pos[-2]+math.cos(ang), pos[-1]+math.sin(ang)])
ang += i*math.pi/2
其中dragon
是先前生成的列表,例如[1, 1, -1, 1, 1, -1, -1]
。我添加[0]以在最后采取额外步骤。对于19次迭代,我的脚本输出:
array of length 1048575 completed in 0.00567 seconds
dragon created in 2.82039 seconds
dragon drawn in 0.01462 seconds
image saved in 0.01229 seconds
我们可以清楚地看到上面的代码是最耗时的。 是否有更快的方法从我们之前生成的列表中生成所有这些点?
答案 0 :(得分:3)
由于sin
和cos
的结果为零,一个或减一,以及循环,您可以查找它们,模4:
pos = [100, 100]
direction = 0
east_west_lookup = [0, -1, 0, 1]
north_south_lookup = [1, 0, -1, 0]
for i in dragon + [0]:
east_west_step = east_west_lookup[direction % 4]
north_south_step = north_south_lookup[direction % 4]
pos.extend([pos[-2] + east_west_step,
pos[-1] + north_south_step])
direction += i
答案 1 :(得分:2)
Numpy也可以做到这一点,非常有效!使用Peter Wood的答案中的查找表,我们可以为x方向编写:
x0 = 100
directions = np.cumsum(2 - dragon) % 4
dx = np.take([0, -1, 0, 1], directions)
x = np.cumsum(np.r_[x0, dx])
2 - dragon
是在取模数之前消除负值。
答案 2 :(得分:0)
这是一种使用字典查找的方式,我认为它更适合使用tuple
来表示您的位置,并使用list
来表示整个路径。
我没有结果,所以请确保face_dict
设置正确。
dragon = [1, 1, -1, 1, 1, -1, -1]
# format: old-direction -> (add-to-x, add-to-y, new-direction)
# using a list would be faster, but I don't have time to think about it :)
face_dict = {
"EAST": {-1: (1, 0, "NORTH"), 1: (1, 0, "SOUTH")},
"SOUTH": {-1: (0, -1, "EAST"), 1: (0, -1, "WEST")},
"WEST": {-1: (1, 0, "SOUTH"), 1: (1, 0, "NORTH")},
"NORTH": {-1: (0, 1, "WEST"), 1: (0, 1, "EAST")}
}
# indicate a position using a tuple, a tuple represents a single location
# while a list is suitable to represent a path
pos = (100, 100)
def make_path(directions, start_pos):
# create empty path with starting position
path = [None] * len(dragon)
path[0] = start_pos
# this is the current state, which is stored in the list after every step.
current_pos = start_pos
# this is where we're facing right now
face = "EAST"
# each iteration fills a single step in the semi-empty path
for index, direction in enumerate(directions):
move = face_dict[face][direction]
current_pos = (current_pos[0] + move[0], current_pos[1] + move[1])
face = move[2]
path[index] = current_pos
return path
注意:合并Peter Wood's solution可以优化每次迭代的查找。
答案 3 :(得分:0)
一个小的改进,但是在我的系统上节省了10-15%的时间,每次都不计算math.pi/2
。它没有Peter Wood的解决方案快,但如果您决定将dragon
列表打开为0,1和-1以外的值,则可能很有用。
pos = [100, 100]
ang = math.pi/2
turn = ang
for i in dragon + [0]:
pos.extend([pos[-2]+math.cos(ang), pos[-1]+math.sin(ang)])
ang += i*turn
答案 4 :(得分:-1)
也许尝试预先分配列表中的空格,然后完全删除extend
调用。像这样(未经测试):
pos = [100] * len(dragon)*2 + 4
ang = math.pi / 2
for idx, i in enumerate(dragon + [0]):
start = idx*2
pos[start+2] = pos[start]+math.cos(ang)
pos[start+3] = pos[start+1]+math.sin(ang)
ang += i * math.pi / 2