BSGS算法求 高次同余方程:1.可爱的质数 2.计算器

BSGS:

参考视频:https://b23.tv/U1JcUA

给你a,b,p,且p为质数,求x的最小非负整数解。 

1.首先我们会想到使用暴力的方式进行求解。那就是枚举x,从0开始。

那么问题来了。是否会有穷尽呢?还是无尽的枚举下去呢?

由于p是质数,由费马小定理可以知道

a ^ (p - 1) \equiv 1 (mod p)

a ^ 0 \equiv 1 (mod p)

所以幂有长度不超过p的循环节。 暴力枚举是可以得到答案的,时间复杂度为O(p)的。

但是,当p很大的时候呢? 这个时候就可以用到一个时间复杂度为O(sqrt(p))的算法进行求解了。这就是BSGS算法。

首先,设x == i * m - j;   其中 m == sqrt(p)

这个公式可以看成什么呢? 可以看成 x / m 的 除数为 i , 余数为j.

但是为什么定义为-j,而不是+ j呢? 这是为了后面的操作方便。

而由上面红色这句话,可以知道,i 小于等于 sqrt(p).  j 也小于等于 m.

i 小于等于sqrt(p)是因为 i 相当于 x / m 的除数, 所以 不会超过sqrt(p)

j 小于等于sqrt(p) 是因为 如果j大于等于 sqrt(p)的话,可以转化为 新 i == i + 1, 新j = j - sqrt(p); 

知道这些之后有什么用呢?

a ^ (i * m - j) mod (p) == b mod p;

所以 a ^ (i * m)  mod p == b * a ^ j mod p;

于是我们从0~m枚举j.

将对应的 b * a ^ j mod p存入到哈希表中。

但是如果存在两个 b * a ^ j mod p值一样时怎么办?

我们存入更大的那个j即可。 因为我们需要的是最小的x. 所以j越大越好。

枚举完了j以后。

我们在去枚举 i, 

首先通过快速幂的方式求解出a ^ m 次方

然后i 从1开始枚举 ,

得到a ^ (i * m)  mod p的值。看哈希表中是否存在对应的这个值。

如果存在这个值的话,那么对应的i , j 就是我们要求出来的解。

x == i * m - j.

而如果枚举了所有以后依然不存在的话,那就是无解了。

同时这里面还有许多细节。

首先,需要对a , b 进行mod p 处理

当a mod p == 0的时候, 要有解 就必须b mod p == 0,输出最小正整数1. 

如果这个时候b mod p 不等于 0 则无解。

如果b等于1. 则直接输出x == 0. 不用接着往后计算了 。(当然,也可以不对这个进行处理。后面的算法实际上也可以将这个情况下x == 0 求出。)

题目链接:1.可爱的质数    2.计算器

题目:

1.可爱的质数

题目描述

给定一个质数 pp,以及一个整数 bb,一个整数 nn,现在要求你计算一个最小的非负整数 ll,满足 b^l \equiv n \pmod pbl≡n(modp)。

输入格式

仅一行,有 33 个整数,依次代表 p, b, np,b,n。

输出格式

仅一行,如果有 ll 满足该要求,输出最小的 ll,否则输出 no solution

输入输出样例

输入 #1复制

5 2 3

输出 #1复制

3

说明/提示

数据规模与约定

  • 对于所有的测试点,保证 2\le b,n < p<2^{31}2≤b,n<p<231。

 2.计算器

题目描述

你被要求设计一个计算器完成以下三项任务:

  1. 给定 y,z,py,z,p,计算 y^z \bmod pyzmodp 的值;
  2. 给定 y,z,py,z,p,计算满足 xy \equiv z \pmod pxy≡z(modp) 的最小非负整数 xx;
  3. 给定 y,z,py,z,p,计算满足 y^x \equiv z \pmod pyx≡z(modp) 的最小非负整数 xx。

为了拿到奖品,全力以赴吧!

输入格式

输入文件包含多组数据。

第一行包含两个正整数 T,KT,K,分别表示数据组数和询问类型(对于一个测试点内的所有数据,询问类型相同)。

以下 TT 行每行包含三个正整数 y,z,py,z,p,描述一个询问。

输出格式

输出文件包括 TT 行。

对于每个询问,输出一行答案。

对于询问类型 2 和 3,如果不存在满足条件的,则输出 Orz, I cannot find x!

输入输出样例

输入 #1复制

3 1
2 1 3
2 2 3
2 3 3

输出 #1复制

2
1
2

输入 #2复制

3 2
2 1 3
2 2 3
2 3 3

输出 #2复制

2
1
0

输入 #3复制

4 3
2 1 3
2 2 3
2 3 3
2 4 3

输出 #3复制

0
1
Orz, I cannot find x!
0

说明/提示

测试点共分为三类,各类测试点占总测试点的比例如下:

K=K=测试点占比
1120\%20%
2235\%35%
3345\%45%

所有数据均满足:1 \leq y,z,p \leq 10^91≤y,z,p≤109,pp 是质数,1 \leq T \leq 101≤T≤10。

代码实现:

1.可爱的质数

# include <iostream>
# include <unordered_map>
# include <cmath>
using namespace std;

unordered_map<long long,long long> book;


long long qmul(long long a , long long b , long long mod)
{
    long long res = 0;
    while(b)
    {
        if(b & 1)
        {
            res = (res + a) % mod;
        }
        b >>= 1;
        a = (a + a) % mod;
    }
    return res;
}

long long qmi(long long a,long long b,long long mod)
{
    long long res = 1;
    while(b)
    {
        if(b & 1)
        {
            res = qmul(res,a,mod);
        }
        b >>= 1;
        a = qmul(a,a,mod);
    }
    return res;
}


/*
long long qmi(long long a , long long b , long long mod)
{
    int res = 1;
    while(b)
    {
        if(b & 1)
        {
            res  = res * a % mod;
        }
        b >>= 1;
        a = a * a % mod;
    }
    return res;
}
*/

int main()
{
    long long p , b ,n;
    scanf("%lld %lld %lld",&p,&b,&n);

    // 求b ^ x  == n (mod p);
    b = b % p;  // 有可能存在b % p为0的情况,这个时候n % p为0才有解。
    n = n % p;
    if(b == 0)
    {
        if(n == 0)
        {
            printf("1\n");
            return 0;
        }
        printf("no solution\n");
    }

    if(n == 1)  // 特判n == 1 的时候, 那么x就是0
    {
        printf("0\n");
        return 0;
    }
    long long m = ceil( sqrt(p) ); // 取sqrt(p)并向上取整

    // j 从0 开始。枚举b * a ^ j (mod p)

    long long temp = n;
    book[temp] = 1;  // j 为0 的时候结果为p;

    for(long long i = 1 ; i <= m ; i++)
    {
        temp = temp * b % p;
        book[temp] = i + 1;
    }

    // 枚举i从1开始

    //首先使用快速幂求解出a ^ m
    temp = qmi(b,m,p);

    long long t = 1;
    for(long long i = 1 ; i <= m ; i++)
    {
        t = t * temp % p;
        if(book[t] != 0)
        {
            printf("%lld\n",i * m - book[t] + 1);
            return 0;
        }
    }
    printf("no solution\n");
    return 0;
}

 2.计算器

# include <iostream>
# include <cmath>
# include <unordered_map>
using namespace std;

long long qmi(long long a, long long b , long long mod)
{
    long long res = 1;
    while(b)
    {
        if(b & 1)
        {
            res = res * a % mod;
        }
        b >>= 1;
        a = a * a % mod;
    }
    return res;
}


long long exgcd(long long a ,long long b ,long long &x,long long &y)
{
    if(!b)
    {
        x = 1, y = 0;
        return a;
    }
    else
    {
        long long t = exgcd(b,a % b,y,x);
        y -= a / b * x;
        return t;
    }
}

// y ^ x == z (mod p)
long long bsgs(long long y , long long z , long long p)
{
    unordered_map<long long,long long> book;
    book.clear();

    y = y % p;  // 有可能存在y % p为0的情况,这个时候z % p为0才有解。
    z = z % p;

    if(y == 0)
    {
        if(z == 0)
        {
            return 1; // 最小正整数为1
        }
        return -1;
    }

    long long m = ceil( sqrt(p) );

    if(z == 1)
    {
        return 0;
    }
    else
    {
        long long temp = z;
        book[z] = 1;
        for(long long i = 1 ; i <= m ; i++)
        {
            temp = temp * y % p;
            book[temp] = i + 1;
        }

        temp = qmi(y,m,p);
        long long t = 1;
        for(long long i = 1 ; i <= m ; i++)
        {
            t = t * temp % p;
            if(book[t] != 0)
            {
                return i * m - book[t] + 1;
            }
        }
    }
    return -1;
}

int main()
{
    long long t,k;
    scanf("%lld %lld",&t,&k);
    while(t--)
    {
        long long y,z,p;
        scanf("%lld %lld %lld",&y,&z,&p);
        if(k == 1)  //快速幂
        {
            printf("%lld\n",qmi(y,z,p));
        }
        else if(k == 2)
        {
            long long x , k;
            long long temp = exgcd(y,p,x,k);
            if(z % temp)
            {
                printf("Orz, I cannot find x!\n");
            }
            else
            {
                long long t = p / temp;
                x =(( (x / temp) % t) * z ) % t;
                printf("%lld\n", ( x % t + t) % t);
            }
        }
        else
        {
            long long t = bsgs(y,z,p);
            if(t == -1)
            {
                printf("Orz, I cannot find x!\n");
            }
            else
            {
                printf("%lld\n",t);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值