麦森数

问题描述

形如 2p-1的素数称为麦森数,这时 P一定也是个素数。但反过来不一定,即如果 P是个素数。2p-1不一定也是素数。到 1998年底,人们已找到了 37个麦森数。昀大的一个是 P=3021377,它有 909526位。麦森数有许多重要应用,它与完全数密切相关。

你的任务:输入 P (1000<P<3100000) ,计算 2p-1的位数和昀后 500位数字(用十进制高精度数表示)

输入数据

只包含一个整数 P(1000<P<3100000)

输出要求

第 1行:十进制高精度数 2p-1的位数。第 2-11行:十进制高精度数 2p-1的昀后 500位数字。(每行输出 50位,共输出 10行,不足 500位时高位补 0)

不必验证 2p-1与 P是否为素数。

输入样例

1279

输出样例

386

00000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000 00000000000000104079321946643990819252403273640855 38615262247266704805319112350403608059673360298012 23944173232418484242161395428100779138356624832346 49081399066056773207629241295093892203457731833496 61583550472959420547689811211693677147548478866962 50138443826029173234888531116082853841658502825560 46662248318909188018470682222031405210266984354887 32958028878050869736186900714720710555703168729087

解题思路

由于只要求结果的昀后 500位数字,所以我们不需要计算完整的结果,只需算出昀后 500位即可。因为用每个数组元素存放十进制大整数的 4位,所以本题中的数组昀多只需要 125个元素。

参考程序 (改编自学生提交的程序 ):

#include <stdio.h>

#include <memory.h>

#define LEN 125 //每数组元素存放十进制的 4位,因此数组昀多只要 125个元素即可。

#include <math.h>

/* Multiply函数功能是计算高精度乘法 a * b

结果的末 500位放在 a中

*/

void Multiply(int* a, int* b)

{

 int i, j;

 int nCarry; //存放进位

 int nTmp;

 int c[LEN]; //存放结果的末 500位

 memset(c, 0, sizeof(int) * LEN);

 for (i=0;i<LEN;i++) {

 nCarry=0;

  for (j=0;j<LEN-i;j++) {

   nTmp=c[i+j]+a[j]*b[i]+nCarry;

   c[i+j]=nTmp%10000;

   nCarry=nTmp/10000;

 }

 }

 memcpy( a, c, LEN*sizeof(int));

 }

 int main()

 {

 int i;

 int p;

 int anPow[LEN];   //存放不断增长的 2的次幂

 int aResult[LEN];  //存放昀终结果的末 500位

 scanf("%d", & p);

 printf("%d\n", (int)(p*log10(2))+1);

 //下面将 2的次幂初始化为 2^(2^0)(a^b表示 a的 b次方),

  //昀终结果初始化为 1

anPow[0]=2;

 aResult[0]=1;

 for (i=1;i<LEN;i++) {

  anPow[i]=0;

  aResult[i]=0;

 }

 

 //下面计算 2的 p次方

 while (p>0) { // p = 0则说明 p中的有效位都用过了,不需再算下去

 if ( p & 1 ) //判断此时 p中昀低位是否为 1

  Multiply(aResult, anPow);

  p>>=1;

  Multiply(anPow, anPow);

 }

 

 aResult[0]--; //2的 p次方算出后减 1

 

 //输出结果

 for (i=LEN-1;i>=0;i--) {

  if (i%25==12)

   printf("%02d\n%02d", aResult[i]/100,

 aResult[i]%100);

  else {

   printf("%04d", aResult[i]);

   if (i%25==0)

 printf("\n");

 }

 }

 return 0;

 }

语句 17:j只要算到 LEN - i - 1,是因为 b[i]×a[j]的结果总是加到 c[i+j]上,i+j大于等于 LEN时,c[i+j]是不需要的,也不能要,否则 c数组就越界了。

语句 18: b[i]×a[j]的结果总是要加到 c[i+j]上,此外还要再加上上次更新 c[i+j-1]时产生

的进位。

语句 19:由于 c中的每一元素代表 10000进制数的 1位,所以 c[i+j]的值不能超过 10000。

语句 20: 算出进位。语句 43到语句 48,每次执行循环都判断 ai(i从 0开始)的值是否为 1,如果是,则将最终结果乘以 。接下来再由 算出 。

语句 54: 输出从万进制数的第 124位开始,万进制数的每一位输出为十进制数的 4位,每行只能输出 50个十进制位,所以发现当 i%25等于 12时,第 i个万进制位会被折行输出,其对应的后两个十进制位会跑到下一行。

语句 55: “%02d”表示输出一个整数,当输出位数不足 2位的时候,用前导 0补足到

2位。本行将一个万进制位分两半折行输出。语句 58: 将一个万进制位以十进制形式输出,用前导 0确保输出宽度是 4个字符。语句 59: 满足条件的话就该换行了。常见问题问题一:没有想到用数学公式和库函数可以直接计算结果位数,而是用其他办法大费周

折。问题二:试图用昀简单的办法,做 p次乘以 2的操作,结果严重超时。问题三:没有对数据规模有足够估计,用数组表示十进制大整数而非万进制数,结果超

时。

思考题 7.4 :本题在数组中存储万进制大整数以加快速度。如果存储的是十万进制数,

岂不更快?而且输出时十万进制数的一位正好对应于十进制的 5位,计算折行也会方便很

多。这种想法成立吗?这么写会真的会更方便吗?

posted on 2010-04-16 15:12  蓝牙  阅读(363)  评论(0编辑  收藏  举报