1. 程式人生 > >程式設計師面試100題之八:不要被階乘嚇倒(二進位制表示中最低位1的位置 )

程式設計師面試100題之八:不要被階乘嚇倒(二進位制表示中最低位1的位置 )

http://blog.csdn.net/hackbuteer1/article/details/6690015


階乘(Factorial)是個很有意思的函式,但是不少人都比較怕它,我們來看看兩個與階乘相關的問題:

1、 給定一個整數N,那麼N的階乘N!末尾有多少個0呢?例如:N=10,N!=3 628 800,N!的末尾有兩個0。

2、求N!的二進位制表示中最低位1的位置。

        有些人碰到這樣的題目會想:是不是要完整計算出N!的值?如果溢位怎麼辦?事實上,如果我們從"哪些數相乘能得到10"這個角度來考慮,問題就變得簡單了。

       首先考慮,如果N!= K×10^M,且K不能被10整除,那麼N!末尾有M個0。再考慮對N!進行質因數分解,N!=(2^x)×(3^y)×(5^z)…,由於10 = 2×5,所以M只跟X和Z相關,每一對2和5相乘可以得到一個10,於是M = min(X, Z)。不難看出X大於等於Z,因為能被2整除的數出現的頻率比能被5整除的數高得多,所以把公式簡化為M = Z。

根據上面的分析,只要計算出Z的值,就可以得到N!末尾0的個數。

【問題1的解法一】

       要計算Z,最直接的方法,就是計算i(i =1, 2, …, N)的因式分解中5的指數,然後求和: 

[cpp] view plaincopyprint?
  1. ret = 0;   
  2. for(i = 1; i <= N; i++)   
  3. {   
  4.     j = i;   
  5.     while(j % 5 ==0)   
  6.     {   
  7.         ret++;     //統計N的階乘中那些能夠被5整除的因子的個數   
  8.         j /= 5;   
  9.     }  
  10. }  
ret = 0; 
for(i = 1; i <= N; i++) 
{ 
	j = i; 
	while(j % 5 ==0) 
	{ 
		ret++;     //統計N的階乘中那些能夠被5整除的因子的個數
		j /= 5; 
	}
}

【問題1的解法二】

公式:Z = [N/5] +[N/5^2] +[N/5^3] + …(不用擔心這會是一個無窮的運算,因為總存在一個K,使得5^K > N,[N/5^K]=0。)

公式中,[N/5]表示不大於N的數中5的倍數貢獻一個5,[N/5^2]表示不大於N的數中5^2的倍數再貢獻一個5,……程式碼如下:

[cpp] view plaincopyprint?
  1. ret = 0;   
  2. while(N)   
  3. {  
  4.     ret += N / 5;   
  5.     N /= 5;  
  6. }  
ret = 0; 
while(N) 
{
	ret += N / 5; 
	N /= 5;
}

       問題2要求的是N!的二進位制表示中最低位1的位置。給定一個整數N,求N!二進位制表示的最低位1在第幾位?例如:給定N = 3,N!= 6,那麼N!的二進位制表示(1 010)的最低位1在第二位。

        為了得到更好的解法,首先要對題目進行一下轉化。

首先來看一下一個二進位制數除以2的計算過程和結果是怎樣的。

       把一個二進位制數除以2,實際過程如下:

       判斷最後一個二進位制位是否為0,若為0,則將此二進位制數右移一位,即為商值(為什麼);反之,若為1,則說明這個二進位制數是奇數,無法被2整除(這又是為什麼)。

      所以,這個問題實際上等同於求N!含有質因數2的個數+1。即答案等於N!含有質因數2的個數加1。 實際上N!都為偶數,因為質因數裡面都有一個2,除了1以外,因為1的階乘是1,是個奇數,其他數的階乘都是偶數。。

【問題2的解法一】

由於N! 中含有質因數2的個數,等於 N/2 + N/4 + N/8 + N/16 + …[1],

根據上述分析,得到具體演算法,如下所示:

[cpp] view plaincopyprint?
  1. /* 
  2. 可以先求出N!中2的個數(因為每存在一個2,則在數的  
  3. 最低位多1個0)。因此求1的最低位的位置即為N!中2的個數+1;  
  4. */  
  5. int lowestOnePos(int n)   
  6. {   
  7.     int ret = 0;     //統計n!中含有質因數2的個數  
  8.     while(n)   
  9.     {   
  10.         n >>= 1;   
  11.         ret += n;   
  12.     }   
  13.     return ret+1;  
  14. }  
/*
可以先求出N!中2的個數(因為每存在一個2,則在數的 
最低位多1個0)。因此求1的最低位的位置即為N!中2的個數+1; 
*/
int lowestOnePos(int n) 
{ 
    int ret = 0;     //統計n!中含有質因數2的個數
    while(n) 
    { 
        n >>= 1; 
        ret += n; 
    } 
    return ret+1;
}

【問題2的解法二】

N!含有質因數2的個數,還等於N減去N的二進位制表示中1的數目。我們還可以通過這個規律來求解。

下面對這個規律進行舉例說明,假設 N = 11011,那麼N!中含有質因數2的個數為 N/2 + N/4 + N/8 + N/16 + …

即: 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的個數

小結
任意一個長度為m的二進位制數N可以表示為N = b[1] + b[2] * 2 + b[3] * 22 + … + b[m] * 2(m-1),其中b [ i ]表示此二進位制數第i位上的數字(1或0)。所以,若最低位b[1]為1,則說明N為奇數;反之為偶數,將其除以2,即等於將整個二進位制數向低位移一位。

相關題目
給定整數n,判斷它是否為2的方冪(解答提示:n>0&&((n&(n-1))==0))。

--------------------------------------------------------------------------------

[1] 這個規律請讀者自己證明(提示N/k,等於1, 2, 3, …, N中能被k整除的數的個數)。