「学习笔记」模运算与 BSGS 算法

取模#

取模符号:xmody,表示 x 除以 y 得到的余数。

例如,

5mod3=27mod4=33mod3=0

x 为被除数,y 为除数,z 为余数,则 x=ky+z,k=xy

模运算#

(a+b)modc=(amodc+bmodc)modc(ab)modc=(amodcbmodc)modc(ab)modc=[(amodc)(bmodc)]modc

读入两个数 n,p,现在求 (n!)modp 是多少?(2<p109,1n1000)

(n!)modp=[(n1)!modp×nmodp]modp

#include <iostream>
using namespace std;

int n, p;

int main() {
    cin >> n >> p;
    int ans = 1;
    for (int i = 1; i <= n; ++ i) {
        ans = 1ll * ans * i % p;
    }
    cout << ans << endl;
    return 0;
}

BSGS 算法#

名称有很多,什么北上广深啊,等等,学名叫 baby-step giant-step,即大步小布算法。
我们由一个问题引入

给定三个数 a,b,pp 是质数,解方程 axmodp=b(a,b,p109)

暴力的做法

int solve(int a, int b, int p) {
// O(p)
    int v = 1;
    for (int x = 0; x <= p - 2; ++ x) {
        if (v == b)    return x;
        v = 1ll * v * a % p;
    }
    return -1;
}

ap1modp=1 可知,余数会在 1 处循环。

ap1+kmodp=ap1akmodp=akmodp

对于该方程,要枚举 p1 个数,那我们将这 p1 个数分组,s 为每组的大小。

a0a1a2as1(as)(÷as)asas+1as+2a2s1a2sa2s+1a2s+2a3s1

若第 2 组数中出现了 b,那么在第 1 组中,一定出现了 bas

#include <set>
using namespace std;

int solve(int a, int b, int p) {
    int s = sqrt(p);
    int v = 1;
    set<int> se;
    for (int i = 0; i < s; ++ i) {
        se.insert(v);
        v = 1ll * v * a % p;
    }
    // O(p / s)
    for (int i = 0; i * s <= p; ++ i) { // 看答案是否在第 i 行里面
        // 要看 b * a (-is) 是否在第 0 行出现
        int c = 1ll * b * qpow(qpow(a, i * s, p), p - 2, p) % p;
        if (se.count(c) != 0) {
            int v = qpow(a, i * s, p); // 第 i 行的第一个数
            for (int j = i * s; ; ++ j) { // O(s)
                if (v == b)    return j;
                v = 1ll * v * a % p;
            }
        }
    }
    return -1;
}

复杂度为:O(ps+s)=O(max(ps,s))
若取 s=p,则为 Op

作者:yifan0305

出处:https://www.cnblogs.com/yifan0305/p/17454589.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载时还请标明出处哟!

posted @   yi_fan0305  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示