您有一个装有球的盒子,我们从盒子中拉出所有的球
但是我们一次可以拉一三个
而且提取顺序很重要。
问题是有几种不同的拉球方法?
所以如果:
盒子包含1个球,只有1种方式。
盒子里有2个球,只有1种方式。
盒子包含3个球,有2种方式(一次一拖一或三拖)
盒子包含4个球,共有3种方式:
1111
13
31
给出的结果是,对于7个球,有9种不同的方法从盒子中提取球
所以给了这个问题,盒子里有多少个球,
我想出的解决方案是递归的:
Int calculate(int balls){
If(balls=0) return 0;
If(balls=1) return 1;
If(balls=2) return 1;
If(balls=3) return 2;
If(balls=4) return 3;
return calculate(balls-1) + calculate(balls-3);
}
这是对的吗?
有没有不使用递归的方法?
谢谢
答案 0 :(得分:5)
您的解决方案是正确的。但是,有多种方法可以使用称为动态规划的技术来提高算法的性能。在这种情况下,您可以记住结果,这意味着在使用递归计算每个中间结果之后,将所有中间结果存储在查找表中。这允许通常需要指数时间才能在线性时间内完成的解决方案。这是JavaScript中的示例实现:
function calculate (balls, map = []) {
if (balls in map) {
return map[balls]
}
switch (balls) {
case 0:
return 0
case 1:
return 1
case 2:
return 1
case 3:
return 2
default:
return map[balls] = calculate(balls - 1, map) + calculate(balls - 3, map)
}
}
console.time('dynamic')
console.log(calculate(50))
console.timeEnd('dynamic')
将其与朴素算法进行比较:
function calculate (balls) {
switch (balls) {
case 0:
return 0
case 1:
return 1
case 2:
return 1
case 3:
return 2
default:
return calculate(balls - 1) + calculate(balls - 3)
}
}
console.time('naive')
console.log(calculate(50))
console.timeEnd('naive')
答案 1 :(得分:3)
您不需要记忆(至少不需要所有值)或解决递归就可以为这种情况或类似情况编写非递归程序。
将执行以下操作:
function calculate (balls) {
if (balls=0) return 0; /* Or remove this line */
if (balls<3) return 1;
resMinus3=1; /* The result for i-3 */
resMinus2=1; /* For i-2 */
resMinus1=1; /* And for i-1 */
for(i=3;;++i) {
newRes=resMinus1+resMinus3; /* The recursion formula */
if (i>=balls) return newRes;
resMinus3=resMinus2; /* Shifting results */
resMinus2=resMinus1;
resMinus1=newRes;
}
}
原因是要计算ball的值,您只需要ball-1和ball-3的值,因此您只需要跟踪三个先前的结果即可更新新结果。或者,您可以将其编写为矩阵更新:
[resMinus1;resMinus2;resMinus3] <-[0,1,0;0,0,1;1,0,1]*[resMinus1;resMinus2;resMinus3]
答案 2 :(得分:1)
从link in the comments中,您可以找到以下等式:
a(n)= Sum_ {i = 0..floor(n / 3)}二项式(n-2 * i,i)
function binom(n, k) {
var coeff = 1;
for (var i = n-k+1; i <= n; i++) coeff *= i;
for (var i = 1; i <= k; i++) coeff /= i;
return coeff;
}
function calculate (balls) {
sum = 0;
for (i = 0; i <= Math.floor(balls/3); i++){
sum += binom(balls - 2*i, i);
}
return sum;
}
console.time('someMathGenius')
console.log(calculate(50))
console.timeEnd('someMathGenius')
答案 3 :(得分:1)
对于N个球,您可以在0到地板(n / 3)三连之间拉动。
对于N个球,您可以拉出k个三连冠,您也可以拉出N-3k个单打。
现在,问题已减少到计算可以订购一种类型的k件商品和另一种类型的N-3k件商品的不同方式。这就是choose(k + N-3k,k)=选择(N-2k,k)。
最终答案是从k = 0到select(N-2k,k)的下限(N / 3)的总和。
N=0: choose(0,0) = 1 so there is 1 way of choosing nothing.
N=1: choose(1,0) = 1
N=2: choose(2,0) = 1
N=3: choose(3,0) + choose(1,1) = 1+1 = 2
N=4: choose(4,0) + choose(2,1) = 1+2 = 3
...
N=7: choose(7,0) + choose(5,1) + choose(3,2) = 1 + 5 + 3 = 9