Python 我的短递归函数执行时间太长,如何优化它
我正在尝试在CodeChef上解决此问题:Python 我的短递归函数执行时间太长,如何优化它,python,performance,recursion,Python,Performance,Recursion,我正在尝试在CodeChef上解决此问题: 但是当我提交我的代码时,它显然需要太长的时间来执行,并且说时间已经过了。我不确定我的代码是否效率低下,我觉得不是这样,或者我在I/O方面有问题。有9秒的时间限制,最多解决10个输入,0问题是你的代码将每个递归调用分支为三个新的。这会导致指数行为 然而,最妙的是,大多数调用都是重复的:如果您使用40调用coinProfit,这将级联到: coinProfit(40) - coinProfit(20) - coinProfit(10) -
但是当我提交我的代码时,它显然需要太长的时间来执行,并且说时间已经过了。我不确定我的代码是否效率低下,我觉得不是这样,或者我在I/O方面有问题。有9秒的时间限制,最多解决10个输入,0问题是你的代码将每个递归调用分支为三个新的。这会导致指数行为 然而,最妙的是,大多数调用都是重复的:如果您使用40调用coinProfit,这将级联到:
coinProfit(40)
- coinProfit(20)
- coinProfit(10)
- coinProfit(6)
- coinProfit(5)
- coinProfit(13)
- coinProfit(10)
您看到的是,在这个小示例中重复了大量的工作,coinProfit已经在10上调用了两次
您可以使用动态编程来解决这个问题:存储早期的计算结果,以防止再次在此部分上分支
可以自己实现动态编程,但可以使用@memoize decorator自动实现
现在这个函数做了很多工作,但次数太多了
import math;
def memoize(f):
memo = {}
def helper(x):
if x not in memo:
memo[x] = f(x)
return memo[x]
return helper
@memoize
def coinProfit(n):
a = math.floor(n/2)
b = math.floor(n/3)
c = math.floor(n/4)
if a+b+c > n:
nextProfit = coinProfit(a)+coinProfit(b)+coinProfit(c)
if nextProfit > a+b+c:
return nextProfit
else:
return a+b+c
return n
@memoize转换函数,以便:对于函数,保留已计算输出的数组。如果对于给定的输入,已经计算了输出,则将其存储在数组中,并立即返回。否则,它将按照方法定义进行计算,存储在数组中供以后使用并返回
正如@steveha指出的,python已经有了一个内置的memoize函数,名为lru_cache,可以找到更多信息
最后一点需要注意的是@memoize或其他动态编程结构并不是所有效率问题的解决方案。首先@memoize会对副作用产生影响:假设你的函数在stdout上打印一些东西,那么@memoize会影响打印的次数。其次,还有像SAT这样的问题,@memoize根本不起作用,因为据我们所知,上下文本身是指数级的。这类问题称为NP难问题
问题是您的代码将每个递归调用分支为三个新调用。这会导致指数行为 然而,最妙的是,大多数调用都是重复的:如果您使用40调用coinProfit,这将级联到:
coinProfit(40)
- coinProfit(20)
- coinProfit(10)
- coinProfit(6)
- coinProfit(5)
- coinProfit(13)
- coinProfit(10)
您看到的是,在这个小示例中重复了大量的工作,coinProfit已经在10上调用了两次
您可以使用动态编程来解决这个问题:存储早期的计算结果,以防止再次在此部分上分支
可以自己实现动态编程,但可以使用@memoize decorator自动实现
现在这个函数做了很多工作,但次数太多了
import math;
def memoize(f):
memo = {}
def helper(x):
if x not in memo:
memo[x] = f(x)
return memo[x]
return helper
@memoize
def coinProfit(n):
a = math.floor(n/2)
b = math.floor(n/3)
c = math.floor(n/4)
if a+b+c > n:
nextProfit = coinProfit(a)+coinProfit(b)+coinProfit(c)
if nextProfit > a+b+c:
return nextProfit
else:
return a+b+c
return n
@memoize转换函数,以便:对于函数,保留已计算输出的数组。如果对于给定的输入,已经计算了输出,则将其存储在数组中,并立即返回。否则,它将按照方法定义进行计算,存储在数组中供以后使用并返回
正如@steveha指出的,python已经有了一个内置的memoize函数,名为lru_cache,可以找到更多信息
最后一点需要注意的是@memoize或其他动态编程结构并不是所有效率问题的解决方案。首先@memoize会对副作用产生影响:假设你的函数在stdout上打印一些东西,那么@memoize会影响打印的次数。其次,还有像SAT这样的问题,@memoize根本不起作用,因为据我们所知,上下文本身是指数级的。这类问题称为NP难问题
您可以通过将结果存储在某种缓存中来优化程序。因此,如果结果存在于缓存中,则无需执行计算,否则计算并将值放入缓存中。这样可以避免计算已计算的值。例如
cache = {0: 0}
def coinProfit(num):
if num in cache:
return cache[num]
else:
a = num / 2
b = num / 3
c = num / 4
tmp = coinProfit(c) + coinProfit(b) + coinProfit(a)
cache[num] = max(num, tmp)
return cache[num]
while True:
try:
print coinProfit(int(raw_input()))
except:
break
您可以通过将结果存储在某种缓存中来优化程序。因此,如果结果存在于缓存中,则无需执行计算,否则计算并将值放入缓存中。这样可以避免计算已计算的值。例如
cache = {0: 0}
def coinProfit(num):
if num in cache:
return cache[num]
else:
a = num / 2
b = num / 3
c = num / 4
tmp = coinProfit(c) + coinProfit(b) + coinProfit(a)
cache[num] = max(num, tmp)
return cache[num]
while True:
try:
print coinProfit(int(raw_input()))
except:
break
我只是试着注意到了一些事情。。。这不一定是答案 在我最近的机器上,n=100000计算需要整整30秒。我想这对于您刚刚编写的算法来说是很正常的,因为它一次又一次地计算相同的值,而您没有像其他答案中建议的那样使用缓存优化递归调用 此外,问题的定义相当温和,因为它坚持:每枚比特兰金币上都有一个整数,但这些数字都是向下舍入的。了解这一点后,您应该将函数的前三行转化为:
import math
def coinProfit(n):
a = math.floor(n/2)
b = math.floor(n/3)
c = math.floor(n/4)
这将防止a、b、c在l处变成浮点数Python3
这会让你的计算机疯狂地陷入一个大的递归混乱,即使是最小的n值。我只是尝试了一下,注意到了一些事情。。。这不一定是答案 在我最近的机器上,n=100000计算需要整整30秒。我想这对于您刚刚编写的算法来说是很正常的,因为它一次又一次地计算相同的值,而您没有像其他答案中建议的那样使用缓存优化递归调用 此外,问题的定义相当温和,因为它坚持:每枚比特兰金币上都有一个整数,但这些数字都是向下舍入的。了解这一点后,您应该将函数的前三行转化为:
import math
def coinProfit(n):
a = math.floor(n/2)
b = math.floor(n/3)
c = math.floor(n/4)
这将防止a、b、c变成浮点数Python3,至少这会让你的计算机疯狂地陷入一个大的递归混乱,即使是最小的n值。还有,如果我说我的函数的复杂性将增加三倍,我说的对吗?也就是说,最坏的情况是3^logn?我打赌输入是你的瓶颈。但是1G的9秒是相当公平的时间。这个程序永远不会退出,而while循环将永远继续要求更多的输入。我遗漏了什么吗?当我在最后删除try/except块后尝试你的程序时,我每次都有一个最大的递归深度错误,不管怎样n@superultranova,不是真的。except分支中的中断将退出while循环。另外,如果我说函数的复杂性将增加三次方,那么我是否正确?也就是说,最坏的情况是3^logn?我打赌输入是你的瓶颈。但是1G的9秒是相当公平的时间。这个程序永远不会退出,而while循环将永远继续要求更多的输入。我遗漏了什么吗?当我在最后删除try/except块后尝试你的程序时,我每次都有一个最大的递归深度错误,不管怎样n@superultranova,不是真的。except分支中的中断将退出while循环。您可以使用添加到Python 3.2中的functools中的lru_缓存装饰器,而不是编写自己的memoize函数。对于早于3.2的Python,您可以得到一个实现它的配方:@steveha:更新了一个小注释。我认为模式是如何实现的不是问题的重点,而是它背后的想法。但不管怎么说,评论不错@steveha注意到,不带参数使用的lru_缓存的默认最大大小为128个值。必须设置参数maxsize,以防您需要更多,这里就是这种情况。@Jivan我仍然认为使用标准lru_缓存而不是滚动自己的缓存是一种胜利,即使您确实需要设置maxsize。@steveha我没有说滚动自己的缓存更好。当然要使用lru_缓存,但如果需要,不要忘记设置max_size:您可以使用添加到Python 3.2中的functools中的lru_缓存装饰器,而不是编写自己的memoize函数。对于早于3.2的Python,您可以得到一个实现它的配方:@steveha:更新了一个小注释。我认为模式是如何实现的不是问题的重点,而是它背后的想法。但不管怎么说,评论不错@steveha注意到,不带参数使用的lru_缓存的默认最大大小为128个值。必须设置参数maxsize,以防您需要更多,这里就是这种情况。@Jivan我仍然认为使用标准lru_缓存而不是滚动自己的缓存是一种胜利,即使您确实需要设置maxsize。@steveha我没有说滚动自己的缓存更好。当然要使用lru缓存,但如果需要的话,不要忘记设置max_size:更好的方法是使用Python的//运算符、整数除法:a=n//2等等。甚至在Python2.x中也可用。更好的方法是使用Python的//运算符、整数除法:a=n//2等等。甚至在Python2.x中也可用。