每天一个算法pan之计算阶乘尾部零的个数

本文介绍了如何计算阶乘中尾部零的个数,从初识问题到递归算法的局限,再到BigInteger解决大数计算,最后揭示了利用数学原理——计算5的因子出现次数的最优解法。作者通过自我反思强调了数学思维在解决问题中的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

lintCode题目:设计一个计算n阶乘中尾部0的个数。

我的算法

对于这道题目我的思路就是把阶乘结果计算出来,然后去算尾部0的个数。

public long trailingZeros(long n) {
        // write your code here, try to do it without arithmetic operators.
        long factorialResult = factorialRecursive(n);
        System.out.println(factorialResult);
        long count = 0;
        if(factorialResult == -1 || factorialResult == 1){
            return count;
        }
        
        while(factorialResult%10 == 0){
            count++;
            factorialResult = factorialResult/10;
        }

        return count;
    }
    public long factorialRecursive(long n){
        if(n < 0){
            return -1;
        }
        if(n == 0){
            return 1;
        }
        if(n < 2){
            return n*1;
        }
        // System.out.println(n);
        return n*factorialRecursive(n-1);
    }

这是我的第二道算法题,可以说我完全没有想到要用数学的思路去处理它,只想着按照题目描述的套路走,我的这种解法是需要计算很大量的数据的,并且这种递归的方式计算阶乘在位数太大的时候是不可行的。

算法漏洞

我试了一下这种方式long类型的n最大只能是20,66及以上的话结果都是0,23-65的数结果就不正确。看了下说int类型最大就12,34及以上就输出0。

找了下资料发现有一种可以存更大的数的阶乘计算方式,使用math包下面的big的类型就可以存储更大的数。这里有两个可选类BigInteger和BigDecimal。

在这里有个引伸的知识点,通常我们说的阶乘都是指整数的阶乘,其实小数也是可以进行阶乘的,不过这就涉及到伽玛函数,我对这个不是很懂,就此打住没有去深入了解了。

我选择了类BigInteger,使用其valueOf方法,将原来的long类型数转成大数存储这样其他我还能计算很很很多个阶乘了。但是这样去计算大数类型的时候是不能直接使用符号的+-*/%这类方法的,只能使用BigInteger提供的方法add(加)、subtract(减)、multiply(乘)、divide(整除)、remainder(求余)。

    public BigInteger factorialRecursive(long n){
        if(n < 0){
            return BigInteger.valueOf(-1);
        }
        if(n == 0){
            return BigInteger.valueOf(1);
        }
        if(n < 2){
            return BigInteger.valueOf(n).multiply(BigInteger.valueOf(1));
        }
        // System.out.println(n);
        return BigInteger.valueOf(n).multiply(factorialRecursive(n-1));
    }

然而...当你的n到了八千多的时候java它就报错了啊java.lang.StackOverflowError。所以这样看来为了算一个阶乘尾部0个数去把阶乘结果算出来是不合理的。

最优算法

嗯我又去找了下最优解,发现原来这是个数学题......

1.首先考虑下当需要阶乘的时候,将阶乘的每个数拆成最小因子;

2.这道题是需要尾部0的个数,那什么能组成一个尾部的0呢,2*5产生的;

3.一个阶乘里面的数,5的数量比2的数量要小(毕竟5比2大那么多,2出现两次5才出现一次呢);

4.所以只需要计算5这个素因子的出现次数就行啦。

    public long trailingZeros(long n) {
        long count = 0;
        if(n <= 0){
            return -1;
        }
        
        while(n != 0){
            count += n/5;
            n /= 5;
        }

        return count;
    }

总结:

说实话,我想了挺久没想明白为啥这个是求尾部0的个数,而不是所有0的个数......然后发现自己是真傻,2*5会产生一个尾部的0,那这个尾部的0无论是乘谁都有个尾部0,同时再乘一个尾部0就会多增加一个尾部0,意味着只要有2*5就会产生一个0。

还是需要多多锻炼自己的思维的。毕竟不用会生锈......

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值