Chinaunix首页 | 论坛 | 博客
  • 博客访问: 764444
  • 博文数量: 144
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1150
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-17 14:32
个人简介

小公司研发总监,既当司令也当兵!

文章分类

全部博文(144)

分类: LINUX

2017-06-15 10:56:10

题目描述

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

任务:从文件中输入P(1000

输入格式

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

输出格式

第一行:十进制高精度数2^P-1的位数。

第2-11行:十进制高精度数2^P-1的最后500位数字。(一行输出,不足500位时高位补0)

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

分析
1. 500位的数只能用数组或字符串表示,由于涉及到计算,用数组表示更方便;
2. 如果M=10^500, 那么对于任意大的整数N,N%M 即为N的后500位数;
3. 假如T1 = N1%M,其中N1是500位以内的数,那么T1也是500位以内的数;令N2是一个32bit可以表达的整数,那么(N1*N2)%M=(T1*N2)%M
4. 对于题目中给的N=2^P(P可能是一个较大的数),令K=2^27 (32bit只能最大表达 2^32 -1,那么实际上我们只能用到2^31, 考虑到K会乘一个10以内的数,需要预留4bit,那么K的指数只能取到27=31-4), 那么N可以表示为:N=K*K*...*K*(2^(P%27)。

    那么:
    (2^P)%M = {K*K*...*K*(2^(P%27) }%M
    将上式分步递归计算:T=(T*K)%M
    最终的T就是我们期望的2^P的后500位数

经过上面分析,获取2^P的后500位就不难实现了。

另外,我还有一个问题,那就是计算2^P的位数。
2^P是一个很大的数,怎么用10进制数表达?最常用的就是科学计数法:
    2^P=r * 10^H ( 1.0 <= r < 10.0)
那么H+1就是我们所求的位数。
对上式两边取10的对数:
    H+log10(r) = log10(2^P) = P*log10(2)
    因为0<=log10(r) < 1, 那么 H <=  P*log10(2) < H+1
    那么:
    H =
floor(P*log10(2))

实现代码:

点击(此处)折叠或打开

  1. #incldue <math.h>
  2. //Mersenne number
  3. static void bigNumberMutil_500limit(unsigned int *rcd, int len, unsigned int key)
  4. {
  5.     int i;
  6.     unsigned long long carry = 0;
  7.     unsigned long long sum;

  8.     for (i = 0; i < len; i++)
  9.     {
  10.         sum = rcd[i] * key + carry;
  11.         rcd[i] = sum % 10;
  12.         carry = sum / 10;
  13.     }
  14. }

  15. void printMersenneNumber(int exp)
  16. {
  17.     int len = (int)floor(log10(2) * exp) + 1;
  18.     unsigned int rcds[501] = { 0 };
  19.     unsigned int mt = (unsigned int)(exp2(27));
  20.     int i;

  21.     rcds[0] = 1;
  22.     while (exp >= 27)
  23.     {
  24.         bigNumberMutil_500limit(rcds, 501, mt);
  25.         exp -= 27;
  26.     }
  27.     if (exp > 0)
  28.     {
  29.         mt = (unsigned int)(exp2(exp));
  30.         bigNumberMutil_500limit(rcds, 501, mt);
  31.     }

  32.     if (rcds[0] > 0)
  33.     {
  34.         rcds[0] -= 1;
  35.     }
  36.     else
  37.     {
  38.         int carry = -1;
  39.         int sum;

  40.         for (i = 0; i < 500 && carry < 0; i++)
  41.         {
  42.             sum = rcds[i] + carry + 10;
  43.             rcds[i] = sum % 10;
  44.             carry = sum / 10 - 1;
  45.         }
  46.     }

  47.     printf("%d\n", len);
  48.     for (i = 499; i >= 0; i--)
  49.     {
  50.         printf("%c", '0' + rcds[i]);
  51.     }
  52.     printf("\n");
  53. }


   


阅读(2662) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~