【洛谷 1045 & 藍橋杯 算法訓練 ALGO-26】麥森數(二分 + 高精度)

1 題目

問題描述

形如 2P12 ^ {P}-1 的素數稱爲麥森數,這時 PP 一定也是個素數。但反過來不一定,即如果 PP 是個素數, 2P12 ^ {P}-1 不一定也是素數。到1998年底,人們已找到了37個麥森數。最大的一個是 P=3021377P=3021377 ,它有 909526909526位。麥森數有許多重要應用,它與完全數密切相關。
任務:從文件中輸入 P1000<P<3100000P(1000<P<3100000),計算 2P12 ^ {P}-1 的位數和最後 500500 位數字(用十進制高精度數表示)

輸入格式

文件中只包含一個整數 PP(1000<P<3100000)

輸出格式

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

樣例輸入

1279

樣例輸出

386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087

2 題目分析

1.題目難點:

  • 2P12 ^ {P}-1 的位數
  • 高精度算法
  • 二分快速冪實現

2.求 2P12 ^ {P}-1 的位數

  • 首先要知道的是 2P12 ^ {P}-12P2 ^ {P} 有相同的位數,因此接下來我們直接求 2P2 ^ {P} 的位數
  • 不妨設 k=2pk\,=\,2^{p}
  • 我們知道, 10n10^{n} 的位數爲 n+1n+1,而根據對數運算規則,10lg2=210^{\lg 2}\,=\,2
  • 因此將 10lg2=210^{\lg 2}\,=\,2 代入 k=2pk\,=\,2^{p}k=10lg2pk = 10^{\lg 2*p} ,所以 2P2 ^ {P} 的位數爲 plg2+1p*{\lg 2} +1
  • 另外,C++ 的 cmath 庫中自帶 log10() 函數。也有直接計算位數的函數:ceil(n次方*log10(底數))

3.高精度算法

  • 在 C/C++ 中,intint 類型有10位,取值範圍爲:21474836482147483647-2147483648\sim2147483647longlonglong\,long 類型有19位,取值範圍爲:92233720368547758089223372036854775807-9223372036854775808\sim9223372036854775807

  • 如果有兩個30位的數字相乘,應該如何用計算機實現?

  • 這就是高精度乘法的應用,以高精度加法舉例,就是用數組存儲數字每一位的數字,逐個運算,具體的算法可參考博客:高精度加、減、乘、除算法實現詳解

  • 在本題中,就是運用高精度乘法的原理實現二分快速冪過程。

4.二分快速冪實現

3 題解

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
//乘法開2倍長度
const int maxn = 1001;
int ans[maxn], b[maxn], carry[maxn], p;

void fun1(){
	memset(carry, 0, sizeof(carry));
	for(int i = 1;i <= 500;i++){
		for(int j = 1;j <= 500;j++){
			carry[i + j - 1] += ans[i] * b[j];
		}
	}
	for(int i = 1;i <= 500;i++){
		carry[i + 1] += carry[i] / 10;
		carry[i] = carry[i] % 10;
	}
	memcpy(ans, carry, sizeof(ans));
}

void fun2(){
	memset(carry, 0, sizeof(carry));
	for(int i = 1;i <= 500;i++){
		for(int j = 1;j <= 500;j++){
			carry[i + j - 1] += b[i] * b[j];
		}
	}
	for(int i = 1;i <= 500;i++){
		carry[i + 1] += carry[i] / 10;
		carry[i] = carry[i] % 10;
	}
	memcpy(b, carry, sizeof(b));
}

int main(){
	int p;
	cin >> p;
	printf("%d\n", (int)( p * log10(2) ) + 1);
	//賦初值 
	ans[1] = 1, b[1] = 2;
	while(p){
		if(p % 2){
			fun1();
		}
		fun2();
		p /= 2;
	}
	//題目要計算的是2 ^ {P} - 1
	ans[1] -= 1;
	for(int i = 500;i > 0;i--){
		if(i != 500 && i % 50 == 0){
			printf("\n%d", ans[i]);
		}else{
			printf("%d", ans[i]);
		}
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章