我正在尝试在CodeChef上解决此问题:http://www.codechef.com/problems/COINS
但是当我提交我的代码时,执行起来显然需要很长时间,并说时间已经过期了。我不确定我的代码是否效率低下(它对我来说似乎没有)或者我是否遇到I / O问题。有9秒的时间限制,以解决最多10个输入,0 <= n <= 1 000 000 000。
在Byteland,他们有一个非常奇怪的货币体系。
每个Bytelandian金币上都写有整数。一枚硬币 n可以在银行中兑换成三个硬币:
n/2
,n/3
和n/4
。但 这些数字都是四舍五入的(银行必须赚钱)。您还可以出售美元的Bytelandian硬币。找的零钱 费率是1:1。但你不能买Bytelandian硬币。
你有一枚金币。美元的最高金额是多少? 你能得到它吗?
这是我的代码:输入1 000 000 000
似乎需要太长时间def coinProfit(n):
a = n/2
b = n/3
c = 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
while True:
try:
n = input()
print(coinProfit(n))
except Exception:
break
答案 0 :(得分:9)
问题在于您的代码将每个递归调用分支为三个新调用。这导致了指数行为。
但不错的是,大多数电话都是重复的:如果您使用coinProfit
拨打40
,这将级联为:
coinProfit(40)
- coinProfit(20)
- coinProfit(10)
- coinProfit(6)
- coinProfit(5)
- coinProfit(13)
- coinProfit(10)
你看到的是重复了很多努力(在这个小例子中,在coinProfit
已经两次调用10
)。
您可以使用动态编程来解决此问题:存储较早的计算结果,以防止您在此部分上再次分支 。
可以实现他/她自己的动态编程,但可以使用@memoize
装饰器自动执行此操作。
现在这个功能在很多时候都做了很多工作。
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
函数,可以找到更多信息here。
最后需要注意的是
@memoize
或其他动态编程构造,并不是所有效率问题的解决方案。首先@memoize
可以对副作用产生影响:假设您的函数在stdout
打印,然后使用@memoize
这会对打印一些东西的次数。其次,有一些问题,比如SAT问题@memoize
完全不起作用,因为上下文本身是指数的(据我们所知)。这些问题被称为 NP-hard 。
答案 1 :(得分:1)
您可以通过将结果存储在某种cache
中来优化程序。因此,如果结果存在于cache
中,则无需执行计算,否则计算并将值放入cache
。通过这种方式,您可以避免计算已计算的值。 E.g。
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
答案 2 :(得分:0)
我只是尝试并注意到了一些事情......这不必被视为 答案。
在我(最近)的机器上,用n = 100 000 000
计算需要30秒。我想你刚刚编写的算法很正常,因为它会再次计算相同的值次数(你没有按照其他答案中的建议用缓存优化你的递归调用)。
此外,问题定义相当温和,因为它坚持:每个Bytelandian金币都有一个整数,但这些数字都是向下舍入 。知道了这一点,你应该将函数的前三行转换为:
import math
def coinProfit(n):
a = math.floor(n/2)
b = math.floor(n/3)
c = math.floor(n/4)
这会阻止a, b, c
转换为float
个数字(至少Python3),这会使您的计算机疯狂变成一个大的递归混乱,即使{{1}的值最小}。