不要被階乘嚇倒[轉]

http://www.kuqin.com/algorithm/20080505/7874.htmlhtml

階乘(Factorial)是個頗有意思的函數,可是很多人都比較怕它,咱們來看看兩個與階乘相關的問題:java

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

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

解答:編程

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

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

根據上面的分析,只要計算出Z的值,就能夠獲得N!末尾0的個數。ui

【問題1的解法一】spa

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

代碼清單2-6

 1 ret = 0;
 2 for(i = 1; i <= N; i++)
 3 {
 4     j = i;
 5     while(j % 5 ==0)
 6     {
 7         ret++;
 8         j /= 5;
 9     }
10 }

【問題1的解法二】

公式:Z = [N/5] +[N/52] +[N/53] + …(不用擔憂這會是一個無窮的運算,由於總存在一個K,使得5K > N,[N/5K]=0。)

公式中,[N/5]表示不大於N的數中5的倍數貢獻一個5,[N/52]表示不大於N的數中52的倍數再貢獻一個5,……代碼以下:

1 ret = 0;
2 while(N)
3 {
4     ret += N / 5;
5     N /= 5;
6 }

 

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

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

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

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

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

因此,這個問題實際上等同於求N!含有質因數2的個數。即答案等於N!含有質因數2的個數加1。

【問題2的解法一】

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

根據上述分析,獲得具體算法,以下所示:

代碼清單2-7

 1 int lowestOne(int N)
 2 {
 3     int Ret = 0;
 4     while(N)
 5     {
 6         N >>= 1;
 7         Ret += N;
 8     }
 9     return Ret;
10 }

【問題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整除的數的個數)。

 網友: Bleakxanadu

一、分解出N!中素數5的個數;

二、分解出N! 中2的個數

網友: jijunchao@gmail

 

今天上午上課又想了想這個問題,對第二個問題也有解法了。

第一題:0的個數等於n/5;

第二題:對於二進制的乘法,要求最低位1的位置,也就是求末尾有幾個0,根據乘法規則,只有乘數末尾帶有0,結果纔會增長0,也就是偶數參與乘法時,末尾的0纔會增長。並且還有個規律,對於2的n次方,它的階乘二進制中最低位1的位置就等於它自己,例如,4的階乘爲24,表示爲11000,最低位1的位置從右邊算起就是第4位。根據這一規律,我寫了一個小算法來求出最低位1的位置,要求輸入n,輸出1的位置。

 1 int find(int n) 
 2 { 
 3          int out,i;
 4          if(n==1)       //1!=1
 5                    out=1;
 6          else if(n==2)  //2!=2  二進制10
 7                    out=2;
 8          else if(n==0) //0!=1,若是我沒記錯的話...
 9                    out=1;
10          else
11          {
12                    for(i=1;!(n/(i*=2)<2););  //求出最逼近n是2的幾回方
13                    out=i-1+find(n%i);        //而後遞歸求輸出位置
14          }
15          return out;
16 }  
17 void main()
18 {
19          int   i = 6;
20          printf("%d",find(i));
21         getchar();  
22 }

程序驗證過應該是正確的。

不過說真的,這個問題若是出如今面試裏面,我極可能答不出來,這要求對二進制運算比較熟悉,看來想去微軟實習還得努力啊,給本身鼓勁!

期待《編程之美》,倒計時2天^_^

歡迎聯繫jijunchao@yahoo.com.cn,但願不吝賜教。

您也能夠訪問www.miniuml.cn,是我之前作的一個uml建模工具,是我本科的主要工做成果之一。

網友: ZXEOC

一、當1<=N<=4的時候N!末尾沒有0,當5<=N<=9的時候N!末尾有1個0,當10<=N<=14的時候N!末尾有2個0,以此類推

緣由:只有5的倍數與偶數相乘以及10的倍數會給最終結果的末尾加上0,而5的倍數比偶數少(偶數就是2的倍數嘛),因此只要計算有多少個5和0結尾的數參與乘法就好了,結果是N/5向下取整

二、假設最低位的1在第x位上,因爲是二進制,因此這個數是2的x-1次方的奇數倍(若是是偶數倍,最低位1的位置就會向前移),換句話說,就是把N!不斷除以2,一直到結果爲奇數,而後算算除了多少次,+1就是結果了

網友: ZXEOC

「這個規律請讀者本身證實(提示N/k,等於1, 2, 3, …, N中能被k整除的數的個數)。」

這個很簡單,因爲1……N是連續的整數數列,而連續的k個數中必然有且只有一個數能夠被k整除,把N/k當作計算把1……N的數列從頭切割成多少個連續k個數的數列就好了

「給定整數n,判斷它是否爲2的方冪」

 網友: ZXEOC

「給定整數n,判斷它是否爲2的方冪」

這個只要把n轉成二進制就好了吧,2的冪的話,二進制表示必定是隻有前面一個1,後面都是0的

PS:2就是二進制裏的「10」,因此這道題就跟求「一個十進制數是否是10的冪」一個道理

網友: Stupidmxx

第一題應該是N/5+N/25+N/125+...嘛

int countOfZero(int N) {

  int base = 5, div = 5, cnt = 0;

  while(div <= N) {

    cnt += N / div;

    div *= base;

  }

  return cnt;

}

 

第二題轉下思路就變成求N的階乘分解因子後2的個數了,把上面程序的base和div改爲2就OK了。不過考慮到除2的特殊性,能夠改寫成以下版本:

int countOfTwo(int N) {

  int cnt = 0, tem = N;

  while(tem != 0) {

    cnt += tem >> 1;

  }

  return cnt;

}

來自:http://www.msra.cn/Articles/ArticleItem.aspx?Guid=a2cdc9c6-4a40-49ca-8765-1d0299367814

 http://www.blogjava.net/Jack2007/archive/2008/10/18/235152.html

相關文章
相關標籤/搜索