我给出了一个公式f(n),其中定义了f(n),对于所有非负整数,如下:
f(0) = 1
f(1) = 1
f(2) = 2
f(2n) = f(n) + f(n + 1) + n (for n > 1)
f(2n + 1) = f(n - 1) + f(n) + 1 (for n >= 1)
我的目标是为任何给定的数字s找到最大的n,其中f(n)= s。如果没有这样的n返回None。 s最高可达10 ^ 25。
我有一个使用递归和动态编程的强力解决方案,但两者都不够高效。哪些概念可以帮助我找到解决这个问题的有效方法?
答案 0 :(得分:1)
我想添加一点复杂度分析并估计f(n)的大小。
如果你看一个f(n)的递归调用,你注意到输入n
基本上除以2,然后再调用f(n)两倍,其中一个调用总是有一个偶数和一个人有一个奇怪的输入
因此,调用树基本上是二叉树,其中特定深度k
上的一半节点总是提供约n / 2 k + 1 的加数。树的深度是log 2(n)。
因此f(n)的值总共约为Θ(n / 2·log 2(n))。
注意:这适用于偶数和奇数输入,但对于偶数输入,该值约为额外的加数n / 2更大。 (我使用Θ-表示法不必考虑一些常量)。
现在的复杂性:
要计算f(n),你必须调用f(n)Θ(2 log 2(n))=Θ(n)次。
因此,如果你想计算f(n)的值直到你达到s(或注意到没有n与f(n)= s)你必须计算f(n)s·log 2(s)次数,是总Θ(s²⋅log(s))。
如果存储f(n)的每个结果,计算f(n)的时间减少到Θ(1)(但它需要更多的内存)。因此总时间复杂度将降低到Θ(s⋅log(s))。
注意:由于我们知道所有n的f(n)≤f(n + 2),因此您不必对f(n)的值进行排序并进行二分搜索。
算法(输入为s
):
l = 1
和r = s
如果你找到了解决方案,那很好。如果不是:再次尝试,但在第2步中转到奇数。如果这也没有返回解决方案,则根本不存在解决方案。
这将使您获得二元搜索的Θ(log(s))和每次计算f(n)的Θ(s),因此总得到Θ(s⋅log(s))。< / p>
正如您所看到的,这与动态编程解决方案具有相同的复杂性,但您无需保存任何内容。
注意:r = s不能作为初始上限保留所有s。但是,如果s足够大,它就会成立。要保存,您可以更改算法:
首先检查,如果f(s)&lt;秒。如果没有,你可以设置l = s和r = 2s(如果它必须是奇数,则设置2s + 1)。
答案 1 :(得分:0)
你能计算f(x)的值,其中x从0到MAX_SIZE只有一次吗?
我的意思是:按DP计算值。
f(0)= 1
f(1)= 1
f(2)= 2
f(3)= 3
f(4)= 7
f(5)= 4
...... ...
f(MAX_SIZE)= ???
如果第1步是非法的,请退出。否则,将值从小到大排序
如1,1,2,3,4,7,......
现在你可以在O(log(MAX_SIZE))时间内找到是否满足f(n)= s。
答案 2 :(得分:0)
2n
和2n+1
的每次迭代中的这种递归都是递增值,因此如果在任何时刻您的值都会大于s
,那么您可以停止算法。< / p>
要制作有效的算法,你必须找到或好的公式,它将计算值,或者在小循环中进行,这将比你的递归更多,更多,更有效。您的递归通常为O(2 ^ n),其中循环为O(n)。 这就是循环的外观:
int[] values = new int[1000];
values[0] = 1;
values[1] = 1;
values[2] = 2;
for (int i = 3; i < values.length /2 - 1; i++) {
values[2 * i] = values[i] + values[i + 1] + i;
values[2 * i + 1] = values[i - 1] + values[i] + 1;
}
在这个循环中添加可能破坏成功的条件。
答案 3 :(得分:0)
不幸的是,你没有提到你的算法应该有多快。也许你需要找到一些非常巧妙的重写你的公式以使其足够快,在这种情况下你可能想在数学论坛上发布这个问题。
根据Master定理,公式的运行时间为f(2n + 1)的O(n)和f(2n)的O(n log n),因为:
T_even(n)= 2 * T(n / 2)+ n / 2
T_odd(n)= 2 * T(n / 2)+ 1
因此整个公式的运行时间为O(n log n)。
因此,如果n是问题的答案,那么这个算法可以在约。 O(n ^ 2 log n),因为你必须大约执行n次公式。
通过存储以前的结果,你可以更快一点,但当然,这是对内存的权衡。
以下是Python中的这种解决方案。
D = {}
def f(n):
if n in D:
return D[n]
if n == 0 or n == 1:
return 1
if n == 2:
return 2
m = n // 2
if n % 2 == 0:
# f(2n) = f(n) + f(n + 1) + n (for n > 1)
y = f(m) + f(m + 1) + m
else:
# f(2n + 1) = f(n - 1) + f(n) + 1 (for n >= 1)
y = f(m - 1) + f(m) + 1
D[n] = y
return y
def find(s):
n = 0
y = 0
even_sol = None
while y < s:
y = f(n)
if y == s:
even_sol = n
break
n += 2
n = 1
y = 0
odd_sol = None
while y < s:
y = f(n)
if y == s:
odd_sol = n
break
n += 2
print(s,even_sol,odd_sol)
find(9992)