第一行:1个正整数n,表示问题的个数,3个大整数p,q,e,其中,p,q均不超过1024比特,整数间用空格分开
接下来n行,每一行一个大整数 c
共n行,每行输出对应的c^d(mod pq)
2 101 103 19
25
73
6724
8380
如何减少循环次数,以2579 mod 100为例,设置窗口大小为5
我们首先预处理,pre[i]存放25i mod 100(i = 1,3,5,7…31) 的值:
25 71 36 78 68 80 5 95 88 56 54 16 1 19 58 92
从左到右遍历每一比特,遇到该位为0时一次选取一位。遇到该位为1时向右选取一些比特位一次性进行计算。规则是:选取的比特位数需要小于窗口长度且最左和最右均为1。
选取后进行的操作:若选取的数字转化为十进制为x,选取的数字的比特位数为j。则进行操作:
for(int i=0;i<j;i++)
{
ans *= ans;
ans %= N;
}
ans *= pre[x];
//来自https://github.com/wsxk/hust_crypto_design/blob/main/%E4%B8%AD%E5%9B%BD%E5%89%A9%E4%BD%99%E5%AE%9A%E7%90%86/ChineseRemainderTheorem.cpp
#include
#include
#define window_size 5
//计算a^paraE % n
void expmod(mpz_t a, mpz_t paraE, mpz_t n){
mpz_t res,tmp;
mpz_init(res);
mpz_init(tmp);
mpz_set_ui(res,1);
mpz_t pre[1<<window_size];
for(int i=0;i<(1<<window_size);i++) mpz_init(pre[i]);
mpz_set_ui(pre[0],1);
mpz_mod(pre[1],a,n);
mpz_mul(tmp,pre[1],a);
mpz_mod(tmp,tmp,n);
for(int i=3;i<(1<<window_size);i+=2){
mpz_mul(pre[i],pre[i-2],tmp);
mpz_mod(pre[i],pre[i],n);
}
unsigned long l = paraE->_mp_size*GMP_LIMB_BITS;
long i=l-1;
long s;
long len;
while (i>=0)
{
if(mpz_tstbit(paraE,i)==0)
{
mpz_mul(res,res,res);
mpz_mod(res,res,n);
i--;
}
else
{
s = (i + 1 - window_size) >= 0 ? (i + 1 - window_size) : 0;
while (mpz_tstbit(paraE,s)==0) s++;
len = i-s+1;
for(int j=1;j<=len;j++)
{
mpz_mul(res,res,res);
mpz_mod(res,res,n);
}
mpz_div_2exp(tmp,paraE,s); //获取向右移s位的结果
unsigned long long temp = mpz_get_ui(tmp)&((1 << len) - 1); //获取所选取的比特位转换为数字的值
mpz_mul(res,res,pre[temp]);
mpz_mod(res,res,n);
i=s-1;
}
}
mpz_set(a,res);
}
int main(){
int n;
mpz_t e,p,q,d,inv_p,inv_q,N,phi_N,phi_q,phi_p,dp,dq,c,ans;
mpz_init(e),mpz_init(p),mpz_init(q),mpz_init(d),mpz_init(c);
mpz_init(inv_p),mpz_init(inv_q),mpz_init(N),mpz_init(phi_N);
mpz_init(phi_q),mpz_init(phi_p),mpz_init(dp),mpz_init(dq);
mpz_init(ans);
scanf("%d",&n);
gmp_scanf("%Zd%Zd%Zd",p,q,e);
mpz_mul(N,p,q);
mpz_sub_ui(phi_q,q,1);
mpz_sub_ui(phi_p,p,1);
//求欧拉函数和d
mpz_mul(phi_N,phi_q,phi_p);
mpz_invert(d,e,phi_N);
//求dp(d mod p-1)和dq(d mod q-1)
mpz_mod(dp,d,phi_p);
mpz_mod(dq,d,phi_q);
//求p逆和q逆
mpz_invert(inv_p,p,q);
mpz_invert(inv_q,q,p);
mpz_t c1,c2;
mpz_init(c1);
mpz_init(c2);
while(n--){
gmp_scanf("%Zd",c);
//计算c1和c2
//c1 = c^dp%p c2 = c^dq%p
mpz_set(c1,c);
mpz_set(c2,c);
expmod(c1,dp,p);
expmod(c2,dq,q);
//最终结果 = c1 * q * inv_q + c2 * p * inv_p
mpz_mul(c1, c1, q);
mpz_mod(c1, c1, N);
mpz_mul(c1, c1, inv_q);
mpz_mod(c1, c1, N);
mpz_mul(c2, c2, p);
mpz_mod(c2, c2, N);
mpz_mul(c2, c2, inv_p);
mpz_mod(c2, c2, N);
mpz_add(c1, c1, c2);
mpz_mod(ans, c1, N);
gmp_printf("%Zd\n",ans);
}
return 0;
}
欢迎指正