麦森数 —— 高精度乘法 与 快速幂综合应用

麦森数 —— 高精度乘法 与 快速幂综合应用

题目描述

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

任务:从文件中输入P(1000<P<3100000),计算2^P-1的位数和最后500位数字(用十进制高精度数表示)
输入
文件中只包含一个整数P(1000<P<3100000)
输出
第一行:十进制高精度数2^P-1的位数。
第2-11行:十进制高精度数2^P-1的最后500位数字。(每行输出50位,共输出10行,不足500位时高位补0)
不必验证2^P-1与P是否为素数。
样例输入 Copy

1279

样例输出 Copy

386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087

题目链接

题目链接 - 麦森数

题解

该题花费我很久的时间啦!功夫不负有心人,终于Ac掉啦且满满的收获!当我Ac那一刻,非常之激动呀!
图片未加载请刷新

言归正传,该题难度蛮大的需要高精度乘法和快速幂的知识,推荐博客:
大数高精度乘法
快速幂
当然理解高精度乘法和快速幂,该题变为So easy!

1,位数解法

首先我们来完成 第一行:十进制高精度数2^P-1的位数。即386位数的要求

我们举个例子来讲解:
比如 一个 100位的十六进制数a 转换为 2进制数,需要多少位?
400 位 = 100 * 4 ,因为大家都知道四个二进位表示一个十六进制位。为什么呢?

一个二进制为只能是 0 或 1 ,表示两种状态 ,一个十六进制位从 0 ~ 15,可以表示16种状态。 知 2 * 2 * 2 * 2 = 16 ,因此需要4 个二进位 来表示十六进制位(即 log 2 ^ 16 == 4)

再如 一个100位的十进制 a 转换成 3 进制数,需要多少位?
答案: 100 * log 3 ^ 10 约等于 209.59 即210位

位数肯定需要向上取整的

位数举一反三

一个十进制数a 需要多少位呢?

12 --> 2 位 log10 ^12 = 1.07918 向上取整2
99 --> 2位 log10 ^ 99 = 1.9956 向上取整2
100 --> 3位 log10 ^ 100 = 2 向上取整2
999 --> 3位 log10 ^ 999 = 2.9995 向上取整3
1001 --> 4位 log10 ^ 1001 =3.0004 向上取整4

因此我们可以解决first problem ,问题是2 ^ p - 1 的十进制有多少位?其实我们完全可以求2 ^ p 的十进制为有多少位,为什么可以呢?

想一想十进制数中啥情况的时候 减1 然后 位数变小呢? 是的,一般是10 , 100, 1000, 10000.。。。。。。,这些十的倍数只有减1 随之位数也会减1 ,其他数都不存在该情况

但是 你见过 哪个2^p数是十的倍数呀,我相信你是没有见过的,嘿嘿

最终位数等于 log 10 ^ (2^p) = p * log 10 ^ 2 ,故只需要一个式子即可求出位数
(math.h库函数提供log函数以e为底的对数函数, log10()函数以10为底的对数函数)

2,500位数的计算

其次我们来完成500位数的计算,此处我们需要快速幂来计算 2 ^ p 来防止超时. 其根本是long long 类型存不下2^p(p 较大时),需用使用高精度来计算大数的500位,此时可以体现大整数乘以大整数的高精度计算的优越性啦!!!

有小伙伴可能会纳闷? 2 ^p 是个非常大的数字,即使高精度计算使用的整型数组可能容纳不了吧!整型数组在内存开辟的空间也有限呀?

是的,没错,但是我们无需将2^p 的超大数的位数都存取来呀。因为有的数相乘是上千万甚至亿位,我们只需要按照题目的要求每次存取后500位即可。最终的结果也只需最后500位的啦

如下将会解决你的困惑!
图片未加载请刷新
图片未加载请刷新
此处选择任意的数字进行测试,最终的结果只取最后两位。

我们可以发现任意的两数字相乘的结果取最后两位数字,再用最后此两位数字乘以其他数字,再取最后结果的两位数字。依次类推,不断的取结果的最后两位。其实不难发现和先把所有数相乘起来再取最后两位数的结果相同。

故我们可以用此方法来求最后500位数字。在相乘的过程中不断的取结果的最后500数,其答案和计算2^p - 1总的超大数结果后取最后500位数字的 结果是一模一样的。

理解以上我讲述的重点地方,以及快速幂和大正数相乘的高精度后 理解代码So easy 的啦!!! 现在我们来看代码吧!

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <stdlib.h>

using namespace std;
const int MAX = 500;
struct Bignum//定义大数的结构体
{
    int len;
    int digit[MAX];
};

Bignum Init(int x)//将整形数值变为结构体
{
    Bignum temp;
    temp.len = 0;
    do {
       temp.digit[temp.len++] = x % 10;
        x = x / 10;
    } while (x);
    return temp;
}

Bignum  mul(Bignum x, Bignum y)//大数相乘
{
    Bignum z;

    memset(z.digit, 0, sizeof(z.digit));
    //用memset来初始化z.digit的500位数字全为 0
    if (x.len + y.len > MAX) z.len = MAX;//如果超过只取500位
    else z.len = x.len + y.len;


    int i, j;
    for (i = 0; i < x.len; i++)
    {
        for (j = 0; j < y.len; j++)
        {
            if (i + j < MAX)//只取前500位
                z.digit[i + j] += x.digit[i] * y.digit[j];
        }
    }

    for (i = 0; i < z.len; i++)//进位,大整数乘法的进位
    {
        z.digit[i + 1] += z.digit[i] / 10;
        z.digit[i] = z.digit[i] % 10;
    }

    while (z.digit[z.len - 1] == 0 && z.len > 0) z.len--;//去除前导 0

    return z;//以大整数的形式返回
}
int main()
{
    int p;
    cin >> p;
    //输入p次方
    
    int m = ceil(p * log10(2));//m为向上取整的值
    printf("%d\n", m);

//初始化res,和a,将变为数组形势
    Bignum res = Init(1);
    Bignum a = Init(2);

//快速幂
    while (p > 0)
    {
        if (p % 2 == 1) res = mul(res , a);

        a = mul(a, a);

        p = p / 2;

    }


    res.digit[0]--;// 最终结果将大数减1,即res数组的0位减1

//------------------------------------------------------
//每按50行进行打印
    int k = 0;
    for (int i = MAX - 1; i >= 0; i--)
    {
        cout << res.digit[i];
        k++;
        if (k % 50 == 0) cout << endl;
    }
    return 0;
}

结语

当然本人能力有限不一定讲的很清楚。若看讲解仍旧一头雾水,此处附上视频链接https://www.bilibili.com/video/BV17f4y127D9
推荐看一看,有助于该题的理解,哈哈哈

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值