不要被階乘嚇到

編程之美有一道關於階乘的題目:
1給定一個整數N,那麼N的階乘等於N!,末尾有多少個0呢,例如N=10,N!=3628800,N!的末尾有兩個0
2求N!的二進制表示中最低位爲1的位置。
階乘定義:

n!={1n(n1)!n=0n>0nN

作者對於問題一的分析:對N!進行質因數分解: N!=2X*3Y*5Z…,因爲10=2*5,所以M與2和5的個數即X、Z有關。每一對2和5都可以得到10,故M=min(X,Z)。因爲能被2整除的數出現的頻率要比能被5整除的數出現的頻率高,所以M=Z。
解法一:
直接計算因式分解中5的指數,然後求和.
代碼實現:
#include <iostream>
using namespace std;
int findZero(int N)
{
   int ret=0;
   for(int i=1;i<=N;i++)
   {
      int j=i;
      //計算每個階乘相乘數字分解因式5的個數
      while(j%5==0)
      {
         ret++;
         j/=5;
      }
   }
   return ret;
}

解法二:
作者根據[N/k]等於1,2,3 ,…,N中能被k整除的數的個數規律,得出下面公式:
Z=[N/5]+[N/52 ]+[N/53 ]+….(總存在一個K,使得5k >N,[N/5k ]=0)
公式中,[N/5]表示不大於N的數中5的倍數貢獻一個5,[N/52 ]表示不大於N的數中52 的倍數再貢獻一個5…。
代碼實現:

#include <iostream>
using namespace std;
int findZero(int N)
{
   int ret=0;
   while(N)
   {
      //計算1,2,3...k能被當前5的指數倍數整除個數
      ret+=N/5;
      //累計5的指數
      N/=5;
   }
   return ret;
}

對於問題二,確實不好想到那方面去,我們都知道在計算機二進制裏,有一個規律,那就是如果一個數是偶數,那麼該數的最後一個二進制必定是0,如果是奇數,那麼該數的最後一個二進制必定是1,這裏作者根據這樣的規律給了兩種解法。
解法一:
判斷最後一個二進制位是否爲0,若爲0,則將此二進制數右移一位,即爲商值;反之,若爲1,則說明這個二進制數是奇數,無法被2整除。
所以,這個問題實際上等同於求N!含有質因數2的個數。即答案等於N!含有質因數2的個數加1。
代碼實現:

#include <iostream>
using namespace std;
int lowestOne(int N)
{
   int ret=0;
   //統計被2整除的個數
   while(N)
   {
      N>>=1;
      ret+=N;
   }
   return ++ret;
}

解法二:
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的個數
代碼實現:

#include <iostream>
using namespace std;
int lowestOne(int N)
{
   int ret=0;
   int temp=N;
   //統計被2整除的個數
   while(temp)
   {
      ret+=temp&1;
      temp>>=1;
   }
   ret=N-ret;
   return ++ret;
}
發佈了119 篇原創文章 · 獲贊 10 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章