1. 程式人生 > 其它 >麥森數(C++)

麥森數(C++)

技術標籤:演算法

麥森數
Description
形如2P-1的素數稱為麥森數,這時P一定也是個素數。但反過來不一定,即如果P是個素數,2P-1不一定也是素數。到1998年底,人們已找到了37個麥森數。最大的一個是P=3021377,它有909526位。麥森數有許多重要應用,它與完全數密切相關。

任務:輸入P(1000<P<3100000),計算2P-1的位數和最後500位數字(用十進位制高精度數表示)。

Input
一個整數P(1000<P<3100000)。
Output

第一行:十進位制高精度數2P-1的位數;
第2-11行:十進位制高精度數2P-1的最後500位數字(每行輸出50位,共輸出10行,不足500位時高位補0);不必驗證2P-1與P是否為素數。

Sample Input
1279

Sample Output
386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560

46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087

(寫個簡略的題解來向自己說明自己已經懂了)
這題的題意好理解,就是輸出2的p次方減一,但是資料太大就要超時,然後還只能用陣列寫。
先把程式碼貼出來:

#include<cstdio>
#include<cstring>
#include<cmath>
typedef long long ll;
int a[1005],b[505];
void ahh(ll x)
{
    if(x==0)return ;
    ahh(x/2);
    for(int i=1;i<=500;i++)
        for(int j=1;j<=500;j++)
    {
        if(x%2==1)a[i+j-1]+=b[i]*b[j]*2;
        else a[i+j-1]+=b[i]*b[j];
    }
    for(int i=1;i<=500;i++)
    {
        b[i]=a[i]%10;
        a[i+1]=a[i+1]+a[i]/10;
    }
    memset(a,0,sizeof(a));
}
int main()
{
    ll p;
    while(scanf("%lld",&p)!=EOF)
    {
        memset(b,0,sizeof(b));
        b[1]=1;
        ahh(p);
        printf("%d\n",(int)(p*log10(2)+1));
        b[1]-=1;
        for(int i=500;i>=1;i--)
        {
            if(i%50==0&&i!=500)printf("\n");
            printf("%d",b[i]);
        }
        printf("\n");
    }
    return 0;
}

首先輸出一個位數,用log函式,是十進位制嘛,所以算出結果對10的對數就好了,很好理解,就比如,如果有一個m數的位數是n,那麼這個數一定大於等於10的n次方,小於等於10的(n+1)次方,那麼它的位數就是n=log10(m)+1

至於下面那個前500位的方陣,就拿樣例來說吧
2的1279次方,如果直接算1279此乘法就會超時,那麼就可以減少算的次數,2的1279次方=2的639次方x2的640次方,這樣就只用乘640次,但是又可以吧639次方分解為319和320…
思路大概就是這樣把p分解開來,每一次分解可以減少一半的運算次數

然後來看程式碼陣列a的作用是用來記錄每次相乘所得到的數,陣列b就是取末尾500位,所以陣列b的大小就是500,陣列a就是兩個陣列b相乘,就要1000

先第一個數b[1]=1,然後ahh(x)函式的作用就是算2的x次方,從樣例來看,要算2的1279次方可以先算2的639次方,然後繼續遞迴就是算319、159、79、39、19、9、4、2、1。

for(int i=1;i<=500;i++)
        for(int j=1;j<=500;j++)
    {
        if(x%2==1)a[i+j-1]+=b[i]*b[j]*2;
        else a[i+j-1]+=b[i]*b[j];

這一串程式碼的作用就是模擬兩個數的交叉相乘
而如果這時候算的x是偶數,那麼2的x次方的結果就是2的(x/2)次方2的(x/2)次方,如果x是奇數,2的x次方就是2的(x/2)次方2的(x/2)次方2,就像512=16x16x2,16=4x4…

然後再把所得結果進位,最後的500位給b陣列,重複遞迴。
最後輸出的時候b[1]記得-1,然後記得換行。

就這樣。