好像有不少更新:)

本文主要记录一些不是那么熟悉的高级数论算法的推导与应用。

exBSGS算法

解决模数、底数不互质的离散对数问题。

(1)为何\(BSGS\)算法不再适用:\(A\)不一定存在逆元,而且无法保证解的循环性。

(2)无解的结论:

设方程为\(A^x=B \pmod{P}\)

当 \((A,P) \nmid B\)且\(B\ne 1\) 时,原方程无自然数解。

还有就是\(A=0,B≠0\)这种。

(3)算法流程:

先判无解。

然后若\(B=1\),显然\(x=0\),特判之。

否则 \(G=(A,P)\)

若\(G>1\),

两边同除以\(G\),得\(A^{x-1}=\frac{B}{G}*(\frac{A}{G})^{-1} \pmod{\frac{P}{G}}\)

迭代至\((A,P)==1\)时即可套用\(BSGS\)算法了。

int BSGS(int a,int b,int P)
{
int s,o;
S.clear();
int m=(int)ceil(sqrt(P));
o=a;
s=b;
for(int i=1; i<=m; i++)
{
s=(ll)s*o%P;
S[s]=i;
}
o=fpow(a,m,P);
s=1;
for(int i=1; i<=m; i++)
{
s=(ll)s*o%P;
if((it=S.find(s))!=S.end())
return i*m-it->second;
}
return -1;
} int exbsgs(int a,int b,int c)
{
a%=c; b%=c;
if(b==1||c==1)return 0;
if(!a)return b?-1:1;
int d=gcd(a,c);
if(d==1)return BSGS(a,b,c);
if(b%d!=0)return -1;
int o=exbsgs(a,(ll)b/d*Inv(a/d,c/d)%(c/d),c/d);
return o==-1?-1:o+1;
}

快速计算阶乘

即快速计算:

对于质数\(p\),把\(N!\)去掉所有\(p\)因子的部分对\(p^e\)取模,\(p^e\le 10^5\)

(1)为了便于统计出现了多少个p的次幂,先将阶乘中所有p的倍数提出来。可以简单算出:共有

$\displaystyle \lfloor \frac{n}{p} \rfloor $ 个。

这中间每一项都除去p,

可以得到 \(\displaystyle \lfloor \frac{n}{p} \rfloor !\) 。该部分可以选择递归求解。

(2)那么接下来只剩下非p的倍数的几项了。通过简单观察可以知道,剩余几项具有循环节,循环节长度小于\(p^e\) 。

原因是剩余项关于p具有循环节,而

\(x+p^e \equiv x \pmod{p^e}\),

所以可以一起计算。

结果会剩下几项凑不齐一个循环节,但是这几项长度已经小于\(p^e\)了,可以选择暴力乘法求解。

有必要举个例子模拟一下:

\(N=22,p=3,e=2,\)

\(22!=1*2*3*......*22\)

\(=(1*2*4*5*7*8*10*11*13*14*16*17*19*20*22)*3^7*7!\)

\(7!\)递归处理;



\((1*2*4*5*7*8*10*11*13*14*16*17*19*20*22)\)

\(\equiv (1*2*4*5*7*8)^2*(19*20*22) \pmod {3^2}\)

两个括号里的暴力计算;

考虑对 \(p^e\) 以内做预处理,查询复杂度可以做到 \(O(\log_p n)\) 左右。

代码在跟下面一起贴

组合数取模(扩展卢卡斯)

即快速计算\(\dbinom{N}{M} \pmod{P}\),\(N,M\le 10^9,P不一定是质数,P\le 10^9\),但\(P\)中任何一个质因子的总积不超过\(10^5\)

考虑中国剩余定理:

令\(Ans=\dbinom{N}{M}\)

对质因子分开计算有:

\(Ans \equiv a_1 \pmod{p_1^{q_1}}\)

\(Ans \equiv a_2 \pmod{p_2^{q_2}}\)

\(......\)

对于一个式子:

不含\(p_i\)的部分使用上述快速阶乘方法求解。

含有\(p_i\)的重数使用\(\displaystyle \sum_{i=1} \lfloor \frac{n}{p^i} \rfloor\)计算。

然后两部分分开算,乘起来就是对应的\(a_i\)。

最后CRT合并起来就可以了。

\[Ans\equiv \sum a_i \times (\prod_{j\neq i}b_j^{-1} \bmod {b_i}) \times \prod_{j\neq i}b_j \pmod {\prod b_i}
\]

struct bnm {
int p, Mod, fac[1 << 20];
void init(int x, int y)
{
p = x;
Mod = y;
fac[0] = 1;
for(int s = 1; s != Mod; ++s)
if(s % p)
fac[s] = 1LL * fac[s - 1] * s % Mod;
else
fac[s] = fac[s - 1];
}
int Fac(ll x)
{
if(!x)
return 1;
return 1LL * Fac(x / p) * fexp(fac[Mod - 1], x / Mod, Mod) % Mod * fac[x % Mod] % Mod;
}
int operator()(ll n, ll m) {
if(n < m || m < 0) return 0;
int res = Fac(n) * Inv(Fac(m), Mod) % Mod * Inv(Fac(n - m), Mod) % Mod;
int z = 0;
for(ll d = n; d; z += d /= p);
for(ll d = m; d; z -= d /= p);
for(ll d = n - m; d; z -= d /= p);
res = 1LL * res * fexp(p, z, Mod) % Mod;
return res;
}
}Solve;

普通二次剩余

即解方程\(x^2=a \pmod{P}\),P是质数。

(1)勒让德符号的定义

\((\frac{a}{p})=1\):a在模p意义下有二次剩余。

\((\frac{a}{p})=-1\):a在模p意义下无二次剩余。

计算公式:\((\frac{a}{p}) = a^{\frac{p-1}{2}} \pmod p\)

(2)求解二次剩余

随机一个\(a\)使得\(b=\sqrt{a^2-n}\)(此时 \(a^2-n\) 无二次剩余,就直接写成根号形式),然后把\(b\)作为二次域的单位元。

那么\(x=(a+b)^{\frac{p+1}{2}}\)(证明需要二项式定理展开,具体参考这里 !ACdreamer

写一个扩域乘法类就好了。

注意这里\(p\)得是奇质数,但\(p=2\)也是很容易特判的。

二次剩余有一正一负两解,下面代码随便取了一个。

namespace Cipolla
{
inline int Le(int x){return fpow(x,(P-1)/2);} int w;
struct Cp
{
int x,y;
Cp(int a=0,int b=0){x=a;y=b;}
};
Cp operator+(Cp a,Cp b){return Cp((a.x+b.x)%P,(a.y+b.y)%P);}
Cp operator-(Cp a,Cp b){return Cp((a.x-b.x+P)%P,(a.y-b.y+P)%P);}
Cp operator*(Cp a,Cp b){return Cp(((ll)a.x*b.x+(ll)w*a.y%P*b.y)%P,((ll)a.x*b.y+(ll)a.y*b.x)%P);}
Cp operator^(Cp a,int b){
Cp o(1,0);
for(;b;b>>=1) {
if(b&1)o=o*a;
a=a*a;
}
return o;
} int calc(int x)
{
x%=P;
int a;
while(1) {
a=rand();
w=((ll)a*a-x+P)%P;
if(Le(w)==P-1) break;
}
Cp s=Cp(a,1)^((P+1)/2);
return min(s.x,P-s.x);
}
}

原根和阶

奇素数与奇素数的方幂有原根,原根 \(g\) 是阶恰好等于 \(p-1\) 的数,也就是 \(g^x(x\in [0,p-2])\) 与整数 \(y\in [1,p-1]\) 一一对应。

我们一般用暴力枚举的方法求原根,原理类似试除法,判断是否存在 \(d|p-1\) 使得 \(g^d\equiv 1 \pmod p\) 即可。

bool judge(int x)
{
for(int j = 0; j < tot; j ++) //只需对质因子做
if(fexp(x, (P - 1) / c[j], P) == 1)
return false;
return true;
}

对一个任意模数求阶也不是很难,只要对 \(\varphi (m)\) 不断试除保证 \(x^k\equiv 1 \pmod m\)即可。

原根可以转换为单位根,即若 \(n|(\varphi(p)-1)\) 则 \(\omega_n=g^{\frac{\varphi(p)-1}{n}}\)。

常用于单位根反演 \([n|i]=\dfrac{1}{n}\sum_{k=0}^{n-1} \omega_n^{ki}\)

单位根反演可以应用于 \(\text{NTT}\) 以及结合“二项式定理”优化复杂度。

快速乘法取模

某些情况下机器不支持int128,或者毒瘤出题人卡int128常数,就需要写这种 \(O(1)\) 快速乘。

inline ll mul(ll a,ll b,ll p){
ll res=a*b-((ll)((long double)a*b/p+0.5))*p;
return res<0?res+p:res;
}

一定要保证 \(a,b\lt p\) 的时候计算,因为它是由这个保证精度的。

合并同余方程组

\(x \equiv now \pmod m\)

\(x \equiv a \pmod b\)

由第一式得

\(x=now+k*m\)

代入二式

\(now+km \equiv a \pmod b\)

\(mk \equiv a-now \pmod b\)

拿个扩欧就能解出来最小的\(k\)

然后\(now=now+k\times m\)

有一个坑点就是你的\(k\)不要对\(m\)取模,

归纳易证你这个\(now\)一定是最小的正整数解,只要题目保证了啥啥,\(now\)就肯定不会溢出。

但是好多题目中\(m\)都会溢出,变成负数,再取模就会出bug。

当然还是不能排除求出假答案的情况,最后可以 check 一遍。

void excrt(ll a,ll b)
{
ll c=((a-now)%b+b)%b;
ll x,y;
ll d=exgcd(M,b,x,y);
if(c%d!=0) err();
c/=d;
b/=d;
x=(x%b+b)%b;
x=(__int128)x*c%b;
now+=x*M;
M*=b;
}

Miller-Rabin素数测试

二次探测原理:

当p是质数时,则一定有

若\(x^2 \equiv 1 \pmod p\),则\(x=1 \pmod p\)或\(x=-1 \pmod p\)。

这样我们就考虑反过来想办法验证\(n\)是质数。

令\(n=2^k \times m + 1\)(m是奇数),

拿个小质数来进行二次探测,进行\(k\)次迭代即可。

取10-12个质数就足够稳了。

代码21行是二次探测,25行是费马小定理。

int ispr(ll n)
{
if(n<2)
return 0;
for(int i=0; i<12; i++)
if(n%Pr[i]==0)
return n==Pr[i];
ll m=n-1,x,y;
int k=0;
while(~m&1)
{
m>>=1;
k++;
}
for(int i=0; i<12; i++)
{
x=fpow(Pr[i],m,n);
for(int j=0; j<k; j++)
{
y=Mul(x,x,n);
if(y==1&&x!=1&&x!=n-1)
return 0;
x=y;
}
if(x!=1)
return 0;
}
return 1;
}

质因数分解 Pollard-rho

先说一下为大整数 \(N(N\le 10^{18})\) 分解质因子的流程:

(1)弄出来一个 \(N\) 的非平凡因子 \(x\)。

(2)递归分解 \(x\) 和 \(N/x\)。

而Pollard-rho就是高效地寻找非平凡因子的算法。

Pollard-rho基于随机化实现,然后算法的效率保证是“生日悖论”,这里的含义如下:

如果随机一个数 \(x\),那么 \(x\) 是 \(N\) 约数的概率极其微小。

但是我们如果随机出 \(x,y\),那么 \(|x-y|\) 是 \(N\) 约数的概率将显著提高。

(1)不断随机数字\(x\),假如\(gcd(x,n)>1\)直接返回这个\(gcd\)

额样例都跑不出来,没有任何前途。

(2)按照上面讲的这么写,并按照倍增的形式不断调整:

x=y=0;
for(int k=2; ; k<<=1)
{
for(int i=1; i<=k; i++)
{
x=(x*x+sd)%n;
v=gcd(abs(x-y),n);
if(v!=1 && v!=n)
return v;
if(x==y)
return n;
}
y=x;
}

效率玄学地大幅提升,但是依然TLE飞起。。。

(3)然而你发现这个gcd常数巨大,我们把改成用一个模 \(N\) 意义下的变量记一下,这样每隔一会查一次就行!

static int Size(1<<7);
x=y=0;
for(int k=2; ;k<<=1)
{
v=1;
y=x;
for(int i=1; i<=k; i++)
{
x=((u128)x*x+sd)%n;
v=((u128)v*_abs(x-y))%n;
if(i%Size==0)
{
z=gcd(v,n);
if(z>1) return z;
}
}
z=gcd(v,n);
if(z>1) return z;
}

就能过了,常数极小。

Pollard-rho时间复杂度是\(O(n^{0.25})\)

实测每次找质因子内层乘法能稳定在\(10^5\)之内。

其实感觉特别暴力。

数论算法 Plus的更多相关文章

  1. 数论算法 剩余系相关 学习笔记 (基础回顾,(ex)CRT,(ex)lucas,(ex)BSGS,原根与指标入门,高次剩余,Miller_Rabin+Pollard_Rho)

    注:转载本文须标明出处. 原文链接https://www.cnblogs.com/zhouzhendong/p/Number-theory.html 数论算法 剩余系相关 学习笔记 (基础回顾,(ex ...

  2. ACM&OI 基础数论算法专题

    ACM&OI 基础数学算法专题 一.数论基础 质数及其判法 (已完结) 质数的两种筛法 (已完结) 算数基本定理与质因数分解 (已完结) 约数与整除 (已完结) 整除分块 (已完结) 最大公约 ...

  3. $\mathcal{OI}$生涯中的各种数论算法的证明

    嗯,写这个是因为我太弱了\(ORZ\). #\(\mathcal{\color{silver}{1 \ \ Linear \ \ Sieve \ \ Method \ \ of \ \ Prime}} ...

  4. B - Common Divisors (codeforces)数论算法基本定理,唯一分解定理模板

    You are given an array aa consisting of nn integers. Your task is to say the number of such positive ...

  5. 一些数论概念与算法——从SGU261谈起

    话说好久没来博客上面写过东西了,之前集训过于辛苦了,但有很大的收获,我觉得有必要把它们拿出来总结分享.之前一直是个数论渣(小学初中没好好念过竞赛的缘故吧),经过一道题目对一些基础算法有了比较深刻的理解 ...

  6. 数据结构(DataStructure)与算法(Algorithm)、STL应用

    catalogue . 引论 . 数据结构的概念 . 逻辑结构实例 2.1 堆栈 2.2 队列 2.3 树形结构 二叉树 . 物理结构实例 3.1 链表 单向线性链表 单向循环链表 双向线性链表 双向 ...

  7. 算法大全(c,c++)

    http://www.2cto.com/kf/201109/105758.html 算法大全(C,C++)一. 数论算法 1.求两数的最大公约数function gcd(a,b:integer):in ...

  8. C 实现的算法篇

    算法的定义:算法是解决实际问题的一种精确的描述方法,目前,广泛认同的定义是:算法的模型分析的一组可行的确定的和有穷的规则 算法的五个特性:有穷性,确切性,输入,输出,可行性.目前算法的可执行的步骤非常 ...

  9. ACM算法

      一.数论算法 1.求两数的最大公约数 2.求两数的最小公倍数 3.素数的求法 A.小范围内判断一个数是否为质数: B.判断longint范围内的数是否为素数(包含求50000以内的素数表): 二. ...

随机推荐

  1. python爬虫模拟登录的图片验证码处理和会话维持

    目标网站:古诗文网 登录界面显示: 打开控制台工具,输入账号密码,在ALL栏目中进行抓包 数据如下: 登录请求的url和请求方式 登录所需参数 参数分析: __VIEWSTATE和__VIEWSTAT ...

  2. DE1_MSEL

    基础的一般实验:01001(现在用的)或10010 马上换linux,做个记录: sd卡启动linux系统时,启动开关0至4位拨至00000

  3. Controller 和 Action -1

    https://www.cnblogs.com/willick/p/3331521.html MVC 的每个请求都会提交到 Controller 处理.Controller 包含了对请求的逻辑处理,能 ...

  4. Git分支基本命令+coding webhook+lnmp

    首先介绍一写基本的git操作命令: 查看当前项目的远程地址: git remote -v 查看远程地址所有分支: git branch -a 或者 git branch -r 查看本地分支与远程分支的 ...

  5. linux - 数据库 - Connected to an idle instance

    运行 connect /as sysdba; 报错:Connected to an idle instance 原因:配置文件 .bash_profile 中的 ORACLE_HOME 的最后这个 / ...

  6. Activiti+Shiro实战

    有人曾说:人的差距都在业余时间拉开的……嗯,我现在深刻理解着这句话,作为一个程序员,技术男,就得不断学习新的技术,跟上时代步伐,才会让自己更有价值~~~~以下这个项目是个人利用业余时间学习并实践的~如 ...

  7. 集成Log4Net到自己的Unity工程

    需要使用的插件库说明: Loxodon Framework Log4NetVersion: 1.0.0© 2016, Clark Yang=============================== ...

  8. AcWing 802. 区间和 离散化

    https://www.acwing.com/problem/content/804/ #include <iostream> #include <vector> #inclu ...

  9. 1069 The Black Hole of Numbers (20分)

    1069 The Black Hole of Numbers (20分) 1. 题目 2. 思路 把输入的数字作为字符串,调用排序算法,求最大最小 3. 注意点 输入的数字的范围是(0, 104), ...

  10. 19年7月份面试7家公司,整理的java面试题(答案自行百度解决,也是个学习的过程)

    Dubbo与注册中心Zookeeper了解多少ConcurrentHashMap的原理 集合 HashMap 和 HashTable和ConcurrentHashMap的原理以及区别HashMap初始 ...