Bzoj 5296 洛谷 4454 upc 6756 破解D-H协议 这辈子都学不会的数论之BSGS算法

9 篇文章 0 订阅
1 篇文章 0 订阅

题目链接: 落谷:https://www.luogu.org/problemnew/show/P4454

                   BZOJ: https://www.lydsy.com/JudgeOnline/problem.php?id=5296

题目描述

Diffie-Hellman密钥交换协议是一种简单有效的密钥交换方法。它可以让通讯双方在没有事先约定密钥(密码)的情况下,通过不安全的信道(可能被窃听)建立一个安全的密钥K,用于加密之后的通讯内容。
假定通讯双方名为Alice和Bob,协议的工作过程描述如下(其中mod表示取模运算):
协议规定一个固定的质数P,以及模P的一个原根g。P和g的数值都是公开的,无需保密。
Alice生成一个随机数a,并计算A=ga mod P,将A通过不安全信道发送给Bob。
Bob生成一个随机数b,并计算B=gb mod P,将B通过不安全信道发送给Alice。
Bob根据收到的A计算出K=Ab mod P ,而Alice根据收到的B计算出K=Ba mod P。
双方得到了相同的K,即gab mod P。K可以用于之后通讯的加密密钥。
可见,这个过程中可能被窃听的只有A,B,而a,b,K是保密的。并且根据A,B,P,g这4个数,不能轻易计算出K,因此K可以作为一个安全的密钥。
当然安全是相对的,该协议的安全性取决于数值的大小,通常a,b,P都选取数百位以上的大整数以避免被破解。然而如果Alice和Bob编程时偷懒,为了避免实现大数运算,选择的数值都小于231,那么破解他们的密钥就比较容易了。

输入

第一行包含两个空格分开的正整数g和P。
第二行为一个正整数n,表示Alice和Bob共进行了n次连接(即运行了n次协议)。
接下来n行,每行包含两个空格分开的正整数A和B,表示某次连接中,被窃听的A,B数值。

输出

输出包含n行,每行一个正整数K,为每次连接你破解得到的密钥。

样例输入

3 31
3
27 16
21 3
9 26

样例输出

4
21
25

提示

对于30%的数据,2≤A,B,P≤1000。
对于100%的数据,2≤A,B<P<231,2≤g<20,1≤n≤20。

题意 :

给你A B g  p ,让你去求K  k=A^b%p 或者k=B^a%p

就用到了北上广深(BSGS)算法 :https://blog.csdn.net/passer__/article/details/81530354

A=g^a%p  等价于  A%p=g^a%p  等价于A≡g^a(mod p) 就可以套用BSGS算法了

m=sqrt(p)   为啥是sqrt(p)看上博客的那个推算

a=i*m-j;  A=g^i*m-j%p  然后 A * g^j = g^(i*m) %p   然后预处理g^j次方用map或者hash。map操作中如果对于重复的数只会是最近的那个j导致的,也就是说j是最大的那个,这样得到的i*m-j也是最小的,完全Ojbk。

然后i从0-m开始乘以g然后判断是否相等 相等就是那个点,这样就可以求出来a 然后快速幂求一下B^a%p就出来K了。

我数学菜啊,真的菜啊。

测试:落谷单个最大 map:220ms                 hash最大50

           bzoj总耗时     map: 868                  hash 448

            upc 耗时       map:152                     hash 52     

下是map做法:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll g,p;
ll Qpow(ll n,ll m)
{
    n=n%p;
    ll ans = 1;
    while(m)
    {
        if(m&1)
            ans=(ans*n)%p;
        n=(n*n)%p;
        m=m>>1;
    }   
    return ans%p;
}
int main()
{
    int n;
    scanf("%lld%lld",&g,&p);
    ll m=sqrt(p)+1;//分块 
    map<ll,ll>M;//预处理 g^j
    ll ans=1; 
    for(int i=0;i<=m;i++)//预处理到m   最大比p大 
    {
        M[ans]=i;
        ans=(ans*g)%p;
    }
    ll gm=Qpow(g,m); 
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        ll A,B;
        int b;
        scanf("%lld%lld",&A,&B);
        ll temp=(gm*Qpow(B,p-2))%p;
        for(int j=1;j<=m;j++)
        {
            if(M.count(temp))
            {
                b=j*m-M[temp];
                break;  
            }
            temp=(temp*gm)%p;
        } 
        ll Ans=Qpow(A,b);
        printf("%lld\n",Ans);
    } 
    return 0;
} 

贴上我kuangbin巨巨的hash模板

#include<bits/stdc++.h> 
using namespace std;
const int  MOD = 1e5+7;
int hs[MOD],head[MOD],Next[MOD],id[MOD],top;
void insert(int x,int y)
{
    int k = x%MOD;
    hs[top] = x, id[top] = y, Next[top] = head[k], head[k] = top++;
}
int find(int x)
{
    int k = x%MOD;
    for(int i = head[k]; i != -1; i = Next[i])
        if(hs[i] == x)
            return id[i];
    return -1;
}
int BSGS(int a,int b,int n)
{
    memset(head,-1,sizeof(head));
    top = 1;
    if(b == 1)return 0;
    int m = sqrt(n*1.0), j;
    long long x = 1, p = 1;
    for(int i = 0; i < m; ++i, p = p*a%n)insert(p*b%n,i);
    for(long long i = m; ;i += m)
    {
        if( (j = find(x = x*p%n)) != -1 )return i-j;
        if(i > n)break;
    }
    return -1;
}
long long Qpow(long long n,long long m,long long p)//快速幂 
{
    n=n%p;
    long long ans = 1;
    while(m)
    {
        if(m&1)
            ans=(ans*n)%p;
        n=(n*n)%p;
        m=m>>1;
    }   
    return ans%p;
}
int main()
{
    int g,n;
    long long a,b,p;
    scanf("%d%lld",&g,&p);
    scanf("%d",&n);
    while(n--)
    {
        scanf("%lld%lld",&a,&b);
        long long ans = BSGS(g,b,p);
        ans=Qpow(a,ans,p);
        printf("%lld\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值