Pra*_*nth 8 python big-o tuples space-complexity python-3.x
这是我学校过去一年中期论文的问题.下面附有一张图,用于显示机器人如何从同一张纸上移动.我的担忧在橙色部分中说明.
基本上,只要机器人遇到左侧未看到的网格方块,机器人就会向前移动并向左转.
给予机器人横向3号网格的指令序列是:('F','T','F','T','F','F','T','F',' F','T','F','F','F')其中'F'表示向前移动一个方格,'T'表示向左转90度.请注意,最后一条指令会导致机器人退出网格.函数gen_seq将网格的大小作为输入,并返回机器人横向网格的指令序列.指令序列是一个包含字符串'F'和'T'的元组,它们代表forward和turn命令.
提供函数gen_seq的递归或迭代实现.提示:Recall int可以与元组相乘.
说明实施时间和空间的增长顺序,并解释您的答案.
这些是markscheme中建议的答案.
def gen_seq(n): # recursive
if n == 1:
return ('F',)
else:
side = ('T',) + (n-1)*('F',)
return gen_seq(n-1) + side + side + ('F',)
def gen_seq(n): # iterative
seq = ('F',)
for i in range(2, n+1):
side = ('T',) + (n-1)*('F',)
seq += side + side + ('F',)
return seq
Run Code Online (Sandbox Code Playgroud)
时间:O(n ^ 3).在每个函数调用(递归)或循环(迭代)中,创建螺旋的每个"层"的路径长度的新元组.由于螺旋的长度是n ^ 2,并且有n个函数调用或循环运行n次,因此总时间是n ^ 2*n = O(n3).换句话说,它是平方和:1 ^ 2 + 2 ^ 2 + 3 ^ 2 + ::: + n ^ 2 = O(n ^ 3)
空间:O(n ^ 2).当天结束时,会创建一个大小为n ^ 2的新元组并返回.
1)我是否正确地推断形成元组的时间复杂度的推导似乎是每次递归/迭代之后更新元组的长度的总和.
如果我想形成大小为n ^ 2(拉直螺旋的大小)的元组,则必须首先形成1 ^ 2,然后形成2 ^ 2 ... n ^ 2,从而得到上述平方和.
我看到了一个关于字符串而不是元组的相关帖子,在这种情况下,time = 1 + 2 + ... n = n ^ 2,这支持我的推理.
2)对于涉及切片/连接的递归函数的空间复杂度,空间等于它们的时间,在这种情况下是O(n ^ 3),这是正确的.我对这种情况的解释是:由于有n个递归占用了堆栈上的空间,并且每次递归都会形成一个长度为n ^ 2的新元组,这是由连接形成的(这里没有切片),空间将是O(n*N ^ 2).
我还认为O(n ^ 2)的建议空间仅适用于迭代解决方案,其中没有观察到堆栈帧,并且只有最终元组的长度(n ^ 2)包含在空间中.
TLDR:您的时间复杂度是正确的,尽管您的递归空间 O(n^3)gen_seq太悲观(它仍然更加浪费)。请注意,最佳静态解决方案是 O(n^2),因为这是答案的大小。如果不需要静态答案,空间复杂度可以降低到O(1)。
让我们从建立一些基本的复杂性开始。以下适用于时间和空间复杂度:
n为 O(n) 的元组。
n为 和的元组m是 O(n+m)。
n^2和的元组m,其结果为 O(n^2+m) = O(n^2)。迭代:
def gen_seq(n): # iterative
seq = ('F',)
for i in range(2, n+1):
side = ('T',) + (i-1)*('F',) # note: `i-1` instead of `n-1`
seq += side + side + ('F',)
return seq
Run Code Online (Sandbox Code Playgroud)
复杂性的关键点是:
range(const, n+1)循环的时间复杂度为O(n),空间复杂度为 O(1)。side对于 i->n,以 n 的大小重新构造。空间被重用,最多为O(n) space。所有 n 次迭代都会消耗时间,即 O(n*n) = O(n^2) 时间。seq在所有 n 次迭代中 与 n 元组连接。空间被重用,最多 O(n*n) = O(n^2) space。所有 n 次迭代都会消耗时间,即 O(n*n^2) = O(n^3) 时间。最大的复杂度获胜,因此迭代使用O(n^2) 空间和O(n^3) 时间。
递归:
def gen_seq(n): # recursive
if n == 1:
return ('F',)
else:
side = ('T',) + (n-1)*('F',)
return gen_seq(n-1) + side + side + ('F',)
Run Code Online (Sandbox Code Playgroud)
复杂性的关键点是:
side以 n 的大小重新构造。空间不会被重用,因为每个空间side都是在递归之前构造的,最多为 O(n*n) = O(n^2) 空间。所有 n 次迭代都会消耗时间,即 O(n*n) = O(n^2) 时间。return值在所有 n 次迭代中与一个 n 元组连接。空间被重用,因为每个return值都是在递归后构造的,最多 O(n*n) = O(n^2) 空间。所有 n 次迭代都会消耗时间,即 O(n*n^2) = O(n^3) 时间。最大的复杂度获胜,因此迭代使用O(n^2) 空间和O(n^3) 时间。
时间复杂度的限制是每个步骤的结果必须在下一步中重复。在 Python 中,可以使用生成器来避免这种情况 - 这允许您返回中间结果并继续生成更多结果:
def gen_seq(n):
yield 'F'
for i in range(1, n):
yield 'T'
yield from ('F' for _ in range(i))
yield 'T'
yield from ('F' for _ in range(i))
seq = tuple(gen_seq(m))
Run Code Online (Sandbox Code Playgroud)
复杂性的关键点是:
range(n)循环的时间复杂度为O(n),空间复杂度为 O(1)。yield from ... range(i)循环的时间为 O(n),空间为 O(1)。空间重用将其保留为O(1) space。重复 n 次给出 O(n*n) = O(n^2) 时间。tuple最大的复杂度获胜,因此迭代使用 O(n^2) 空间和 O(n^2) 时间。如果不存储结果而是直接使用,则仅使用 O(1) 空间。
| 归档时间: |
|
| 查看次数: |
622 次 |
| 最近记录: |