快速模幂,大数模小数

https://ac.nowcoder.com/acm/contest/330/E

链接:https://ac.nowcoder.com/acm/contest/330/E
来源:牛客网
 

精通程序设计的 Applese 叕写了一个游戏。


在这个游戏中,有一个 n 行 m 列的方阵。现在它要为这个方阵涂上黑白两种颜色。规定左右相邻两格的颜色不能相同。请你帮它统计一下有多少种涂色的方法。由于答案很大,你需要将答案对 109+7109+7 取模。

输入描述:

仅一行两个正整数 n, m,表示方阵的大小。

输出描述:

输出一个正整数,表示方案数对 10^9+7 取模。

示例1

输入

复制

1 1

输出

复制

2

示例2

输入

复制

2 2

输出

复制

4

由题意知所求的只是2^n mod 1000000007即可

根据数论知识有A^B mod P = A ^  ( euler(P) + B mod euler(P) )  mod P 即可

由此可以避免掉使用高精度的麻烦。

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
typedef long long ll;
const int mod=1000000007;
int c;
char str[100005];
void quick_mod(ll n){
    ll a=2,k=1;
    while(n){
        if(n%2==1){
            k=k*a;
            k%=mod;
        }
        a=a*a%mod;
        n/=2;
    }
    cout<<(k*2)%mod<<endl;
}
void euler(int n){ //返回euler(n)
     int res=n,a=n;
     for(int i=2;i*i<=a;i++){
         if(a%i==0){
             res=res/i*(i-1);
             while(a%i==0) a/=i;
         }
     }
     if(a>1) res=res/a*(a-1);
     c=res;
}
char sss[100005];
int main()
{
    euler(mod);
        ll b=0;
        scanf("%s%s",str,sss);
        int len=strlen(str);
        for(int i=len-1;i>=0;i--){
            if(str[i]=='0'){
                str[i]='9';
            }
            else{
                str[i]=(char)((int)(str[i])-1);
                break;
            }
        }
        for(int i=0;i<len;i++){
            b=b*10+str[i]-'0';
            if(b>c)
                b=b%c;
        }
        quick_mod(b+c);
    return 0;
}

或:

#include<bits/stdc++.h>
using namespace std;
char x1[200005];char x2[200005];
typedef long long ll;
ll pow(ll x,ll n,ll mod)
{
    ll res=1;
	while(n>0)
	{
	   if(n%2==1)
	   {
	   	 res=res*x;
	   	 res=res%mod;
	   }
	   x=x*x;
	   x=x%mod;
	   n>>=1;
	}
	return res;
}
ll n,m;
const ll P=1000000007;
ll f(char s[])
{
    int len=strlen(s);
    ll tmp=1;
    ll res=0;
    for(int i=len-1;i>=0;i--)
    {
        res=(res+((s[i]-'0')*tmp%(P-1)))%(P-1);
        tmp=(tmp*10)%(P-1);
    }
    return res;
}
int main()
{
    scanf("%s%s",x1,x2);
    n=f(x1);
    ll ans=pow(2,n+1000000006,P);
    cout<<ans<<endl;
    return 0;
}

 

<think>我们正在讨论运算(Modular Exponentiation),这是一种在密码学(尤其是RSA算法)中至关重要的运算。运算指的是计算形如 \( a^b \mod m \) 的表达式,其中 \( a \) 是底数,\( b \) 是指数, \( m \) 是数。其核心挑战在于当 \( a, b, m \) 非常大时(例如在RSA中,\( b \) 可能是一个2048位的整数),直接计算 \( a^b \) 再取是不可行的,因为 \( a^b \) 会超出计算机的整数表示范围。因此,我们需要高效的算法。 ### 一、运算的数学原理 运算基于以下运算的性质: 1. **乘法性质**: \( (a \times b) \mod m = [(a \mod m) \times (b \mod m)] \mod m \) 2. **运算性质**: \( a^b \mod m \) 可以通过将指数 \( b \) 分解为二进制形式,然后利用平方和乘法逐步计算。 ### 二、快速算法(平方乘算法) 快速算法(也称为平方乘算法)将指数 \( b \) 表示为二进制形式,例如 \( b = (b_k b_{k-1} \dots b_1 b_0)_2 \),其中 \( b_i \) 是二进制位(0或1)。那么: \[ a^b = a^{\sum_{i=0}^{k} b_i \cdot 2^i} = \prod_{i=0}^{k} \left( a^{2^i} \right)^{b_i} \] 因此,我们可以通过迭代的方式计算: - 从结果 \( res = 1 \) 开始。 - 遍历指数 \( b \) 的每一个二进制位(从最低位到最高位): - 如果当前位为1,则将结果乘以当前的底数 \( a \) 并取 \( m \)。 - 然后将底数平方并取 \( m \)(因为 \( a^{2^{i+1}} = (a^{2^i})^2 \))。 - 指数右移一位(相当于除以2)。 **算法步骤**: 1. 初始化结果 \( res = 1 \)。 2. 将底数 \( a \) 对 \( m \) 取(减少计算量)。 3. 当指数 \( b > 0 \) 时循环: - 如果 \( b \) 是奇数(即最低位为1),则 \( res = (res \times a) \mod m \)。 - 将 \( a \) 平方: \( a = (a \times a) \mod m \)。 - 将 \( b \) 右移一位(即 \( b = b // 2 \))。 4. 返回 \( res \)。 **时间复杂度**:由于指数 \( b \) 的二进制位数是 \( \log_2 b \),所以循环执行 \( O(\log b) \) 次,每次执行常数次乘法和取运算。因此,总时间复杂度为 \( O(\log b) \),这对于大指数非常高效。 ### 三、运算在密码学中的应用 在RSA加密和解密过程中,核心操作就是运算: - **加密**: \( c = m^e \mod n \) - **解密**: \( m = c^d \mod n \) 其中 \( e \) 和 \( d \) 都是非常大的数(通常1024位以上),因此必须使用快速算法。 此外,运算的逆问题(即已知 \( a, m \) 和 \( a^b \mod m \),求 \( b \))称为**离散对数问题**,目前没有已知的多项式时间算法求解(在经典计算机上),这构成了公钥密码学(如Diffie-Hellman密钥交换、ElGamal加密)的安全基础[^1]。 ### 四、实现示例 以下是用C语言实现的快速算法(运算): ```c #include <stdio.h> // 函数:计算 a^b mod m long long mod_exp(long long a, long long b, long long m) { long long res = 1; a = a % m; // 先取,确保a<m while (b > 0) { // 如果b是奇数,则乘上当前的a if (b % 2 == 1) { res = (res * a) % m; } // 将指数右移一位(相当于除以2) b = b >> 1; // 将底数平方 a = (a * a) % m; } return res; } int main() { long long a = 2, b = 10, m = 1000; printf("%lld^%lld mod %lld = %lld\n", a, b, m, mod_exp(a, b, m)); // 输出 2^10 mod 1000 = 24 return 0; } ``` ### 五、注意事项 1. **大整数处理**:在实际密码学应用中,\( a, b, m \) 可能非常大(例如2048位),因此需要使用大整数库(如GMP)来避免溢出。 2. **常数时间实现**:在安全敏感的场景中,算法执行时间不应泄露指数 \( b \) 的信息(例如,避免通过时间侧信道攻击推断私钥)。因此,需要实现常数时间的快速算法(通常通过固定循环次数和避免条件分支来实现)。 3. **算法优化**:进一步优化包括使用蒙哥马利约简(Montgomery Reduction)来加速乘运算。 ### 总结 运算是密码学中的基础运算,其高效实现依赖于快速算法。该算法通过指数的二进制分解,将乘法和取操作控制在 \( O(\log b) \) 次,从而能够处理极大的指数。在RSA、Diffie-Hellman等公钥密码系统中,运算直接关系到系统的安全性和效率。 §§相关问题§§ 1. 如何用快速算法计算矩阵的? 2. 离散对数问题在密码学中还有哪些应用? 3. 如何优化运算以抵抗侧信道攻击? 4. 在RSA算法中,为什么选择65537作为公钥指数? 5. 除了快速算法,还有哪些方法可以计算大数?</think>### 运算的数学原理与实现详解 运算(Modular Exponentiation)是密码学中的核心运算,指计算形如 \( a^b \mod m \) 的表达式。其核心在于**高效计算大指数运算结果**,同时避免中间值溢出。下面从数学原理和实现方法两方面详细说明: --- #### 一、数学原理 1. **运算性质** 运算基于以下关键性质: \[ (a \times b) \mod m = [(a \mod m) \times (b \mod m)] \mod m \] 这一性质允许我们将大数运算分解为多个小数运算的组合[^1]。 2. **指数分解原理** 任意指数 \( b \) 可表示为二进制形式: \[ b = \sum_{i=0}^{k} b_i \cdot 2^i \quad (b_i \in \{0,1\}) \] 因此: \[ a^b = a^{\sum b_i \cdot 2^i} = \prod_{i=0}^{k} \left( a^{2^i} \right)^{b_i} \] 例如: \[ a^{11} = a^{8+2+1} = a^8 \times a^2 \times a^1 \] 3. **离散对数难题** 已知 \( a^b \mod m \) 求 \( b \) 是**离散对数问题**,在经典计算机上无多项式时间解法,这是公钥密码学的安全基础[^1]。 --- #### 二、快速算法实现 快速算法通过**二进制分解指数+迭代平方**实现 \( O(\log n) \) 时间复杂度: ##### 算法步骤(迭代法): 1. 初始化结果 \( res = 1 \) 2. 将底数 \( a \) 对 \( m \) 取: \( a \leftarrow a \mod m \) 3. 当指数 \( b > 0 \) 时循环: - 若 \( b \) 为奇数: \( res \leftarrow (res \times a) \mod m \) - 将 \( a \) 平方: \( a \leftarrow (a \times a) \mod m \) - 将 \( b \) 右移一位: \( b \leftarrow b // 2 \) 4. 返回 \( res \) ##### 代码实现(C语言): ```c #include <stdio.h> int mod_exp(int base, int exp, int mod) { int res = 1; base %= mod; // 初始取 while (exp > 0) { if (exp & 1) // 指数为奇数 res = (res * base) % mod; base = (base * base) % mod; // 底数平方 exp >>= 1; // 指数右移 } return res; } int main() { printf("2^10 mod 1000 = %d\n", mod_exp(2, 10, 1000)); // 输出 24 return 0; } ``` ##### 示例计算 \( 3^{13} \mod 7 \): | 步骤 | 指数 \( b \) (二进制) | \( a \) | \( res \) | |------|------------------------|---------|--------------------| | 初始 | 1101 (13) | 3 | 1 | | 1 | 1101 (奇) | 3 | \( 1×3 \mod 7=3 \) | | | 右移→110 | \( 3^2 \mod 7=2 \) | | | 2 | 110 (偶) | 2 | 3 | | | 右移→11 | \( 2^2 \mod 7=4 \) | | | 3 | 11 (奇) | 4 | \( 3×4 \mod 7=5 \) | | | 右移→1 | \( 4^2 \mod 7=2 \) | | | 4 | 1 (奇) | 2 | \( 5×2 \mod 7=3 \) | | 结果 | | | **3** | --- #### 三、密码学中的应用 1. **RSA加密/解密** - 加密: \( c = m^e \mod n \) - 解密: \( m = c^d \mod n \) 依赖快速处理大指数(如2048位)[^1][^3]。 2. **Diffie-Hellman密钥交换** 计算 \( g^{ab} \mod p \) 建立共享密钥,其中 \( a,b \) 为私钥。 3. **数字签名** DSA和ECDSA签名验证涉及运算。 --- #### 四、关键优化技术 1. **蒙哥马利约简** 通过预计算加速乘运算,适用于硬件实现。 2. **滑动窗口法** 将指数分组处理,减少乘法次数(如处理连续0比特)。 3. **中国剩余定理(CRT)** 在RSA解密中,将运算分解为: \[ m = [ (c^{d_p} \mod p) \cdot q \cdot q^{-1} + (c^{d_q} \mod q) \cdot p \cdot p^{-1} ] \mod n \] 速度提升4倍[^3]。 --- #### 五、安全性注意事项 1. **侧信道攻击防御** - 常数时间实现:避免条件分支泄露指数信息 - 盲化技术:添加随机数掩码 2. **大数选择原则** - 数 \( m \) 至少2048位 - 指数 \( b \) 需足够大(如RSA中 \( e>2^{16} \)) > 快速算法将指数运算从 \( O(n) \) 优化到 \( O(\log n) \),使1024位RSA解密在普通CPU上仅需毫秒级[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值