通过迭代创建列表的最快方法
Fastest way of creating a list through iteration
我最近创建了一个脚本来创建 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
可以明显看出上面的代码是最耗时的。
有没有更快的方法从我们之前生成的列表中生成所有这些点?
或许尝试在列表中预分配 space,然后完全删除 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
由于sin
和cos
的结果都是零,一,负一,循环的,可以查一下,取四:
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
这里有一种使用字典查找的方式,我觉得用一个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
注意:结合可以优化每次迭代的查找。
一个小的改进,但在我的系统上节省了 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
Numpy 也可以做到这一点,非常高效!使用 Peter Wood 的答案中的查找 table 我们可以写出 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
是取模前去掉负值
我最近创建了一个脚本来创建 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
可以明显看出上面的代码是最耗时的。 有没有更快的方法从我们之前生成的列表中生成所有这些点?
或许尝试在列表中预分配 space,然后完全删除 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
由于sin
和cos
的结果都是零,一,负一,循环的,可以查一下,取四:
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
这里有一种使用字典查找的方式,我觉得用一个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
注意:结合
一个小的改进,但在我的系统上节省了 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
Numpy 也可以做到这一点,非常高效!使用 Peter Wood 的答案中的查找 table 我们可以写出 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
是取模前去掉负值