细胞分裂算法——递归

1,886 阅读5分钟

问题:

一个细胞,一小时分裂一次(一个变两个🥚),生命周期是三小时,求n小时后有多少细胞?

解法一:动态规划(拆分子问题)

先说明这个解法不咋好用,存在性能问题(我还专门画了图😓),希望可以给小伙伴们带来一些借鉴意义吧😊

分析

由题意可知,细胞生命周期共四个阶段,不如假设细胞消亡: 白色 -> 绿色 -> 黄色 -> 黑色

  • 白色:新细胞,分裂一次后产生一个白色细胞,自己则变成绿色

  • 绿色细胞再次分裂出一个白色细胞,自己则变成黄色

  • 黄色细胞再次分裂出一个白色细胞,自己则变成黑色,黑色即生命周期结束

  • 存活的细胞 = 白色 + 绿色 + 黄色,就是所求。

分析示意图

image.png

该思路,经过上面的分析,问题转化成几个子问题,即:分别计算出黄色、绿色和白色细胞的数量

代码展示

// 细胞分裂算法
/*
一个细胞,一小时分裂一次,生命周期是三小时,求n小时后有多少细胞?
细胞消亡: 白色 -> 绿色 -> 黄色 -> 黑色
白色:新细胞,分裂一次后产生一个白色细胞,自己则变成绿色
绿色细胞再次分裂出一个白色细胞,自己则变成黄色
黄色细胞再次分裂出一个白色细胞,自己则变成黑色,黑色即声明周期结束
存活的细胞 = 白色 + 绿色 + 黄色
*/

function total(n) {
  var yellow = function (n) {
    // 设置出口, 边界条件
    if (n === 0 || n === 1) return 0
    return green(n - 1)
  }

  var green = function (n) {
    // 设置出口, 边界条件
    if (n === 0) return 0
    return white(n - 1)
  }

  var white = function (n) {
    // 设置出口, 边界条件
    if (n === 0) return 1
    return white(n - 1) + green(n - 1) + yellow(n - 1)
  }
  return yellow(n) + green(n) + white(n)
}

console.log(total(0));
console.log(total(1));
console.log(total(2));
console.log(total(3));
console.log(total(4));
console.log(total(5));
/*
1
2
4
7
13
24
123
*/

小结

上面这个解法存在问题,没有“备份”,存在重复计算,测试发现 n>30 计算就非常非常困难了。我还没想出来怎么修改(后面我再看一下),读者可以借鉴一下这种思路,有大佬可以修正的烦请告知一下,在这先行谢过啦😃

解法二:——归纳法推荐✨

数学归纳法:由个别推出一般。归纳法适用于想解决一个问题转化为解决他的子问题,而他的子问题又变成子问题的子问题,而且我们发现这些问题其实都是一个模型,也就是说存在相同的逻辑归纳处理项

分析:

  • 每次细胞分裂产生的新细胞(最新鲜的🥚),等于分裂前(上一次)的细胞总数(重点

  • 未开始计时之前,都是1个细胞, f(n) = 1, n <= 0

  • 未开始有细胞死亡时,f(n) = 2 * n, n < 3

  • 开始有细胞死亡时,f(n) = 2 * f(n - 1) - 死亡细胞数 ,n >= 3

时间(小时)细胞总数(个)
01
12
24
37
413
524
644

关键在于求出死亡细胞数的表达式,下面且看我推演分析👇

已知细胞生命周期是三小时,所以:

  1. 第3小时后,第0小时的细胞死亡(思维陷阱 ❗),So, f(3) = 2 * f(2) - f(0),这里的死亡细胞数是f(0)

依据f(3),姑且把公式定为: f(n) = 2 * f(n-1) - f(n-3),(偷偷告诉你这是不对的,继续看👇)

  1. 第4小时后,第1小时的细胞死亡,So, f(4) = 2 * f(3) - f(1)

显然这是不对的,因为f(1)两个细胞中的一个是f(0)的,在f(3)的时候已经死亡,不应该计算在内。所以要减去的这个死亡细胞数 = f(1)中的新生细胞数 ✔

  1. 第5小时后,第2小时的细胞死亡,但f(2)的4个细胞中2个已经死亡,所以死亡细胞数 = f(2)中的新生细胞

👉 从上面的分析来看,死亡细胞数并不是前3小时的细胞总数f(n-3) ,因为n-3时刻细胞总数 = 新生细胞 + 老细胞,老细胞在n时刻之前就已经死亡了。

因此,n时刻的死亡细胞数 应该是n-3时刻新生的细胞数,而n-3时刻新生的细胞数正是n-4时刻的总细胞数,即f(n-4),

🎉 所以,开始有细胞死亡时的正确计算公式是: f(n) = 2 * f(n-1) - f(n-4)

相比较,这个思路的整体代码就简单多了,请看👀

代码展示

function total02(n) {
  if (n <= 0) {
    return 1;
  }
  if (n < 3) {
    return 2 * n;
  }
  if (n >= 3) {
    return 2 * total02(n - 1) - total02(n - 4);
  }
}
console.log("归纳法:");
console.log("0=", total02(0));
console.log("1=", total02(1));
console.log("2=", total02(2));
console.log("3=", total02(3));
console.log("4=", total02(4));
console.log("5=", total02(5));
console.log("6=", total02(6));
console.log("50=", total02(50));

/*
归纳法:
0= 1
1= 2
2= 4
3= 7
4= 13
5= 24
6= 44
50= 19426970897100
*/

小结

归纳包括完全归纳法和不完全归纳法。数学归纳法是一种完全归纳法。

从归纳法的过程不难看出,递归的数学模型其实就是归纳法。由个别推出一般,恰好跟演绎法相反,演绎法是从一般推出个别,所有数学证明都是演绎法。


🎈🎈🎈

🌹 本篇完,关注我,你会发现一个踏实努力的宝藏前端😊,让我们一起学习,共同成长吧。

🎉 喜欢的小伙伴记得点赞关注收藏哟,回看不迷路 😉

✨ 欢迎大家转发、评论交流

🧨 蟹蟹😊

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿