【洛谷 1045 & 蓝桥杯 算法训练 ALGO-26】麦森数(二分 + 高精度)

目录

    • 1 题目
        • 问题描述
        • 输入格式
        • 输出格式
        • 样例输入
        • 样例输出
    • 2 题目分析
    • 3 题解

1 题目

问题描述

形如 2 P − 1 2 ^ {P}-1 2P1 的素数称为麦森数,这时 P P P 一定也是个素数。但反过来不一定,即如果 P P P 是个素数, 2 P − 1 2 ^ {P}-1 2P1 不一定也是素数。到1998年底,人们已找到了37个麦森数。最大的一个是 P = 3021377 P=3021377 P=3021377 ,它有 909526 909526 909526位。麦森数有许多重要应用,它与完全数密切相关。
任务:从文件中输入 P ( 1000 < P < 3100000 ) P(1000P1000<P<3100000,计算 2 P − 1 2 ^ {P}-1 2P1 的位数和最后 500 500 500 位数字(用十进制高精度数表示)

输入格式

文件中只包含一个整数 P P P(1000

输出格式

第一行:十进制高精度数 2 P − 1 2 ^ {P}-1 2P1 的位数。
第2-11行:十进制高精度数 2 P − 1 2 ^ {P}-1 2P1 的最后500位数字.(每行输出50位,共输出10行,不足500位时高位补0)
不必验证 2 P − 1 2 ^ {P}-1 2P1 P P P 是否为素数。

样例输入

1279

样例输出

386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087

2 题目分析

1.题目难点:

  • 2 P − 1 2 ^ {P}-1 2P1 的位数
  • 高精度算法
  • 二分快速幂实现

2.求 2 P − 1 2 ^ {P}-1 2P1 的位数

  • 首先要知道的是 2 P − 1 2 ^ {P}-1 2P1 2 P 2 ^ {P} 2P 有相同的位数,因此接下来我们直接求 2 P 2 ^ {P} 2P 的位数
  • 不妨设 k   =   2 p k\,=\,2^{p} k=2p
  • 我们知道, 1 0 n 10^{n} 10n 的位数为 n + 1 n+1 n+1,而根据对数运算规则, 1 0 lg ⁡ 2   =   2 10^{\lg 2}\,=\,2 10lg2=2
  • 因此将 1 0 lg ⁡ 2   =   2 10^{\lg 2}\,=\,2 10lg2=2 代入 k   =   2 p k\,=\,2^{p} k=2p k = 1 0 lg ⁡ 2 ∗ p k = 10^{\lg 2*p} k=10lg2p ,所以 2 P 2 ^ {P} 2P 的位数为 p ∗ lg ⁡ 2 + 1 p*{\lg 2} +1 plg2+1
  • 另外,C++ 的 cmath 库中自带 log10() 函数。也有直接计算位数的函数:ceil(n次方*log10(底数))

3.高精度算法

  • 在 C/C++ 中, i n t int int 类型有10位,取值范围为: − 2147483648 ∼ 2147483647 -2147483648\sim2147483647 21474836482147483647 l o n g   l o n g long\,long longlong 类型有19位,取值范围为: − 9223372036854775808 ∼ 9223372036854775807 -9223372036854775808\sim9223372036854775807 92233720368547758089223372036854775807

  • 如果有两个30位的数字相乘,应该如何用计算机实现?

  • 这就是高精度乘法的应用,以高精度加法举例,就是用数组存储数字每一位的数字,逐个运算,具体的算法可参考博客:高精度加、减、乘、除算法实现详解

  • 在本题中,就是运用高精度乘法的原理实现二分快速幂过程。

4.二分快速幂实现

  • 快速幂原理:https://blog.csdn.net/liangllhahaha/article/details/82119378
  • 在本题中,用两个函数实现快速幂中 ans *= basebase *= base 的功能,其中 p p p 则为幂方 b b b ,数组 b[maxn] 为基数 base

3 题解

#include 
#include 
#include 
#include 
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;
}

你可能感兴趣的:(算法/数据结构,蓝桥杯)