[可能有用科技]更多 GCD 算法
前言#
重写旧文 & 增加新内容。
欧几里得算法#
int gcd(int a,int b) {
return b ? gcd(b,a % b) : a;
}
非递归 :
int gcd(int a,int b) {
int p = a % b;
while(p)
a = b,b = p,p = a % b;
return b;
}
更相减损法#
可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。
白话 :
- 将被 整除的一直除以 直到因数不包含 。
- 用较大的数减去较小的,直到减数和差相等,得到的就是最大公因子。
- 同时除了 次 的情况,要在结尾乘以
int gcd(int x,int y) {
if(!x || !y) return x | y;
if(!(x & 1) && !(y & 1)) return gcd(x >> 1,y >> 1) << 1;
else if(!(y & 1)) return gcd(x,y >> 1);
else if(!(x & 1)) return gcd(x >> 1,y);
return gcd(abs(x - y),std::min(x,y));
}
非递归 :
int gcd(int x,int y) {
int p = 0;
while(!(x & 1) && !(y & 1)) {
x >>= 1,y>>= 1;
p++;
}
while(!(x & 1)) x >>= 1;
while(!(y & 1)) y >>= 1;
while(x && y) {
if(x > y) std::swap(x,y);
y = y - x;
}
return (1 << p) * (x | y);
}
能不能再给力一点?
尝试快速判断 能除以 几次,众所周知整除 就是二进制末位为 的时候二进制右移一位,那么求出 按位或 的二进制末尾连续 个数即可,这个过程通过跑得飞快的内置函数 __builtin_ctz()
实现.
inline int gcd(int a,int b) {
int k = __builtin_ctz(a | b);
a >>= __builtin_ctz(a);
b >>= __builtin_ctz(b);
while(a && b) {
if(a > b) std::swap(a,b);
b -= a;
}
return a << k;
}
当然可以把这个玩意和欧几里得算法结合一下 :
inline int gcd(int a,int b) {
int k = __builtin_ctz(a | b);
a >>= __builtin_ctz(a);
b >>= __builtin_ctz(b);
int p = a % b;
while(p)
a = b,b = p,p = a % b;
return b << k;
}
能不能再给力一点?
值域预处理#
将值域表示为 ,质数集表示为 。
对于值域较小的情况,可以做到 预处理, 查询。
Theorem 1
可以被分解为一个三元组 满足 , 或 。
Proof 1
归纳法 :
基础 : 对于 有 。
归纳 : 假设现在对于一个数 , 都有解,尝试证明对于 有解。
首先求 的最小质因数 ,可知现在 有解,记为 。
根据定义 ,那么 。
如果 ,我们之前得到 :
然后
并且
那么得到 ,有一组合法的分解为
然后是 的情况。
我们知道 ,那么显然 时 。
于是这时候 显然是 ,一组可行的分解为 。
证毕。
可以发现分解一个数 成三个因数的方式就是首先求其最小质因子 然后求 的分解方案 ,那么 就是 的分解方案了。
然后线性筛的时候一个数会被其最小质因子筛到,那么显然这个分解可以直接线性预处理的。
最后我们回到如何求 。
对于一个 ,将 拆分为 。
首先算出 然后
然后对 重复这个过程即可。
具体令当前细化到求一个 :
如果 就求 使得要求出的两个数都在 范围内,这些 可以 预处理出来。
然后如果 ,如果 那么结果就是 ,如果 那就是 。
最后回顾一下全过程 :
- 预处理 以内的 ,复杂度 。
- 预处理 以内的所有数的分解方案,类似线性筛的方式,复杂度 。
- 询问的时候需要的信息都预处理出来了,复杂度 。
于是就有 的 了。
Code :
为啥我数学相关的代码都巨大常数啊……
V
是值域常量,SV
是值域根号。
struct CommonFac {
bool vis[V + 5];
int pri[V],pcnt;
int fac[V + 5][3];
int G[SV + 5][SV + 5];
void init() {
rep(i,1,SV) {
G[i][i] = G[i][0] = G[0][i] = i;
repl(j,1,i)
G[i][j] = G[j][i] = G[j][i % j];
}
fac[1][0] = fac[1][1] = fac[1][2] = 1;
rep(i,2,V) {
if(!vis[i]) {
fac[i][0] = fac[i][1] = 1;
fac[i][2] = i;
pri[++pcnt] = i;
}
for(int j = 1;j <= pcnt && i * pri[j] <= V;++j) {
int t = i * pri[j];vis[t] = 1;
fac[t][0] = fac[i][0] * pri[j];
fac[t][1] = fac[i][1],fac[t][2] = fac[i][2];
if(fac[t][0] > fac[t][1]) std::swap(fac[t][0],fac[t][1]);
if(fac[t][1] > fac[t][2]) std::swap(fac[t][1],fac[t][2]);
if(!i % pri[j]) break;
}
}
}
inline int gcd(int a,int b) {
if(a <= SV && b <= SV) return G[a][b];
int res = 1;
repl(i,0,3) {
int t;
if(fac[a][i] > SV)
t = (b % fac[a][i]) ? 1 : fac[a][i];
else
t = G[fac[a][i]][b % fac[a][i]];
b /= t,res *= t;
}
return res;
}
}F;
作者:AstatineAi
出处:https://www.cnblogs.com/AstatineAi/p/gcd-algos.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)