从盒子里拿球的不同方法

时间:2018-09-21 02:50:40

标签: algorithm

您有一个装有球的盒子,我们从盒子中拉出所有的球
但是我们一次可以拉一三个 而且提取顺序很重要。
问题是有几种不同的拉球方法?
所以如果:
盒子包含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);
}

这是对的吗?
有没有不使用递归的方法?

谢谢

4 个答案:

答案 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