2、尾部的零

问题描述: 设计一个算法,计算出n阶乘中尾部零的个数。
样例: 11!=39916800 11 ! = 39916800 ,因此应该返回 2。
解答(js实现):

const trailingZeros = function (n) {
    var sum = 0;
    var power = Math.floor(Math.log(n) / Math.log(5));
    for (var i=1;i<=power;i++) {
        sum += Math.floor(n / Math.pow(5, i));
    }
    return sum;
}

分析:思考一个数尾部的零是怎么得来的,乘以10对吧,所以n的阶乘尾部有多少零就可以看成它有多少个10这个因子,而10又可分解为两个质因数相乘 2×5 2 × 5 。将 n! n ! 分解为质因数相乘的形式,大概是这样:

n!=1×2×3×(2×2)×5×(2×3)×7×(2×2×2)×(3×3)×(2×5)... n ! = 1 × 2 × 3 × ( 2 × 2 ) × 5 × ( 2 × 3 ) × 7 × ( 2 × 2 × 2 ) × ( 3 × 3 ) × ( 2 × 5 ) . . .
显然,2的个数会比5的个数多,所以有多少个5就会有多少个10。故而问题就转化为寻找 n! n ! 的结果有多少个5这个因子。
  那么这个怎么找呢?我们看一下上边的等式,现在已经乘到10了,出现了2个5,想象一下继续乘下去的情况,到15时会再出现一个,到20时又会再出现一个,到25时则会出现2个,继续看下去,就会发现一个规律:
  (1) 每逢遇到5的倍数,就会出现一个;
  (2) 每逢遇到5的幂次方的倍数,就会出现5的幂次方的指数个,为了便于计算,我们把遇到5的幂次方的倍数时也算到5的倍数里,这时遇到5的幂次方的倍数就会出现 (5(51)) ( 5 的 幂 次 方 的 指 数 − ( 5 的 幂 次 方 的 指 数 − 1 ) ) 个,也就是1个。
  这样用文字来说会有点绕,我们不妨用代数式说明一下:

  • 5 5 的倍数,遇到一次就出现1个5,会遇到 n5 次,记作 n5×1=n5 n 5 × 1 = n 5 个;
  • 52 5 2 的倍数,遇到一次就会出现2个5,会遇到 n52 n 5 2 次,但是 52 5 2 的倍数又包含在 5 5 的倍数中,所以如果我们按2个算就会每次遇到都多计算1次,因此把那1次减去,就记作 n52×(21) 个;
  • 53 5 3 的倍数,遇到一次就会出现3个5,会遇到 n53 n 5 3 次,但是 53 5 3 的倍数又包含在 5 5 的倍数和 52 的倍数中,所以如果我们按3个算就会每次遇到都多计算2次,因此把那2次减去,就记作 n53×(32) n 5 3 × ( 3 − 2 ) 个;
  • 54 5 4 的倍数,同理,可记作 n54×(43) n 5 4 × ( 4 − 3 ) 个;
  • 依此类推下去。

  根据以上,我们不难得出一个公式:

Sum=n5+n52+n53+n54+ S u m = n 5 + n 5 2 + n 5 3 + n 5 4 + · · ·
  有了这个公式,问题就容易了,只需求出 log5n log 5 ⁡ n ,取整,然后将其作为循环次数累加就行了。
  js中有Math.log()函数,返回一个数的自然对数(loge, 即ln)。虽然没有直接求 log5n log 5 ⁡ n 的方法,但 log5n=lnnln5 log 5 ⁡ n = ln ⁡ n ln ⁡ 5 ,所以n(包括n)以内5的幂次方的最大指数可求得:

var power = Math.floor(Math.log(n) / Math.log(5));

  至此,我们从1开始循环到power,每次循环都加上公式中对应循环次数的项,即可得到结果:

var sum = 0;
for (var i=1;i<=power;i++) {
    sum += Math.floor(n / Math.pow(5, i));
}

总结:由以上的计算方式可以看出,power就是 lnn ln ⁡ n ,所以符合O(logN)的时间复杂度。在做这道题时,也找了不少资料,很多都提到求Sum的那个公式,但是具体怎么来的都没有很清晰地写思路,于是自己就试了试,期间也跑偏了几次,还好最后自圆其说地归纳出来了,希望对看到的朋友有所帮助。
  另外,还有一种思路也能按O(logN)的时间复杂度实现,这篇博客对于另一种思路写得很清晰,博主很用心,推荐一下 ^-^ 。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值