编程之美之"不要被阶乘吓到"

问题陈述

  1. 给定一个整数N,N的阶乘N!中末尾有多少个0?
  2. N! 的二进制表示中最低位1的位置?

问题1的O(N2)解法

如果N! = K x 10M, K是不能被10整除的数。
M 表示 N!的末尾有M个0。

可以看到,N!的末尾有多少个0,和N!的分解式中10的个数有关。

对 N! 进行质因数分解 N! = (2X) x (3Y) x (5Z)......。

因此M只和质因数分解式中2的个数和5的个数有关。即 M = min(X, Z)

由于从1-N中,能被2整除的数出现的频率 肯定大于 能被5整除的数出现的频率。
因此,M = Z;

因此得到我们的时间复杂度O(N2)朴素的解法:

public static int numOfZeroINFactorial_1(int N){
        int res=0;
        for(int i=1; i<=N; ++i){
            int j = i;
            while(j % 5 == 0){
                res++;
                j /= 5;
            }
        }
        return res;
    }

问题1的O(N)解法

k是正整数, floor(N/k)表示 1,2,3,...., N中能被k整除的数的个数

证明
假设1,2,3,...., N中有m个数可以被k整除,则共有
1k, 2k, 3k, ..., mk(mk<=N)个数可以被k整除。显然,m=floor(N / k) 就是正解。

因此,M = floor(N/5) + floor(N / 52) + floor(N / 53) + ... 0.
该式表示,N!中含有质因数5的个数。

public static int numOfZeroINFactorial_2(int N){
        int res=0;
        while(N!=0){
            res += (N/5);
            N /= 5;
        }
        return res;
    }

问题2的O(N)解法1

问题分析

求解N! 的二进制表示中最低位1的位置。等价于 求解N!的二进制表示中最后有多少个0. 原问题的解就是0的个数加1.

将 N! 进行质因数分解 N! = (2X) x (3Y) x (5Z)......后,质因数2的个数就是N!的二进制表示中最后的0的个数

N!中质因数2的个数等于 floor(N/2) + floor(N / 22) floor(N / 23) + ... +0.

代码如下:

public static int solution1(int N){
        int res=0;
        while(N!=0){
            res += (N >> 1);
            N >>= 1;
        }
        return res+1;
    }

问题2的O(N)解法2

N!中质因数2的个数等于N减去N的二进制表示中1的数目

证明
N!中质因数2的个数等于 floor(N/2) + floor(N / 22) floor(N / 23) + ... +0.

假设N=11011则
1101 + 110 + 11 + 1
=(1000 + 100 + 1)
+(100 + 10)
+(10 + 1)
+(1)
=(1000 + 100+ 10 + 1)+(100 + 10 + 1)+ 1
= 1111 + 111 + 1
=(10000 -1)+(1000 - 1)+(10-1)+(1-1)
= 11011 - N二进制表示中1的个数

private static int numOne(int v){
        int num = 0;
        while(v!=0){
            v &= (v-1);
            num++;
        }
        return num;
    }
res = N - numOne(N);

扩展问题1

给定整数N,判断它是否是2的方幂。

N>0 && ( (N&(N-1))==0 )

你可能感兴趣的:(编程之美之"不要被阶乘吓到")