数学小专题
简单复习一下数学的几个模板。
总感觉数学还是要多复习,不然很容易忘记。
简单数论
\(O(\sqrt{n})\)判定质数
不用多讲。
Miller-Rabin算法
bool mr(ll x,ll b)
{
if(qp(b,x-1,x)!=1)
return false;
ll k=x-1;
while((k&1)==0)
{
k>>=1;
ll d=qp(b,k,x);//快速幂,计算b^k mod x
if(d!=1 && d!=x-1)
return false;
if(d==x-1)
return true;
}
return true;
}
bool mr(ll x)
{
if(x==46856248255981)
return false;
if(x==2||x==3||x==7||x==61||x==24251)
return true;
return mr(x,2)&&mr(x,3)&&mr(x,7)&&mr(x,61)&&mr(x,24251);
}
对于一个数\(x\),我们选一个质数\(b\)作为基底,依次计算\(b^{x-1},b^{\frac{x-1}{2}},b^{\frac{x-1}{2^2}},\cdots\)。注意:
- 在第一次计算时不考虑\(b^{x-1} \equiv x-1\)的情况。
- 遇到\(b^{\frac{x-1}{2^k}} \equiv x - 1\)或者\(\frac{x-1}{2^k}\)为奇数的情况就直接判定正确。
注意\(5\)个素因子积:\(2,3,5,7,24251\)。那个很强的伪素数\(46856248255981\)我倒觉得真心没必要背。
约数\(O(\sqrt{N})\)筛法
又叫试除法。每次围绕\(\sqrt{N}\)对称地找到两个因子\(p,q\),把这两个因子加入约数列表中。注意当\(p^2=N\)时要特判一下,不然会加多了。
倍数法
和线性筛的代码比较类似,适合一次筛多个质数的问题。
gcd
\[
\gcd(a,b) = \gcd(b, a \mod b)
\]
埃式筛
比较好理解。
线性筛
for(rg int i = 1; i <= N; ++ i)
{
if(v[i] < 0)
{
v[i] = i;
prime[++ pn] = i;
}
for(rg int j = 1; j <= pn; ++ j)
{
if(prime[j] * i > N || prime[j] > v[i])
break;
v[i * prime[j]] = prime[j];
}
}
暂时还没有试过这个代码。从理论上来说应该是没错的。
进阶数论
冷知识
设一个数的分解式:
\[
N = \prod_{i = 1}^{k}p_i^{\alpha_i}
\]
我们可以把指数提取出来:
\[
N = (\alpha_1,\alpha_2,\cdots)
\]
它可以帮助我们从全新的角度观察数论。其实说白了就是把数论中的运算变成对数形式。
举个例子,求两个数的\(\gcd\):
\[
\gcd((\alpha_i),(\beta_i)) = (\min\{\alpha_i,\beta_i\})
\]
这样我们就可以把方程\(\gcd(x,a)=b\)变成若干个整数不等式了。
再举个例子,整除关系:
\[
(\alpha_i) \big| (\beta_i) \iff \alpha_i \leq \beta_i
\]
这样就也把整除关系变成若干个不等式了。从本质上来说,\(<\mathbb{Z}^{*},|>\)本身就是一个偏序集,因此才会有这样的特性。
可以结合这道题看一下:CSP-S 2009 Hankson的趣味题
重要函数的计算与预处理
欧拉函数\(\phi(n)\)。
int phi(int n)
{
int ret = n;
for(rg int i = 2; i * i <= n; ++ i)
if(n % i == 0)
{
ret = ret / i * (i - 1);
while(n % i == 0)
n /= i;
}
if(n > 1)
ret = ret / n * (n - 1);//n是质数
return ret;
}
这个是直接根据定义式计算的。
它的预处理版本:
void init()
{
for(rg int i = 2; i <= n; ++ i)
phi[i] = i;
for(rg int i = 2; i <= n; ++ i)
if(phi[i] == i)
for(int j = i; j <= n; j += i)
phi[j] = phi[j] / i * (i - 1);
}
当然,还有基于线性筛的版本:
void init()
{
for(rg int i = 2; i <= N; ++ i)
{
if(v[i] == 0)
{v[i] = i, prime[++ pn] = i, phi[i] = i - 1;}
for(rg int j = 1; j <= pn; ++ j)
{
if(prime[j] * i > N || prime[j] > v[i])
break;
v[i * prime[j]] = prime[j];
phi[i * prime[j]] = phi[i] * (i % prime[j] == 0 ? prime[j] : prime[j] - 1);
}
}
}
这里用到了\(\phi(i)\)的两个性质,这里也不多赘述。
莫比乌斯函数\(\mu(n)\)。
for(rg int i = 1; i <= N; ++ i)
mu[i] = 1, v[i] = 0;
for(rg int i = 2; i <= N; ++ i)
{
if(v[i])
continue;
mu[i] = -1;
for(rg int j = (i << 1); j <= N; j += i)
{
v[j] = 1;
if((j / i) % i == 0)
mu[j] = 0;
else
mu[j] *= -1;
}
}
顺带一提,学长zsy讲过一个非常有趣的理解方式。这里不妨粘贴之。
我们都知道莫比乌斯反演:(简便起见,这里直接写成卷积形式)
\[
\begin{aligned}
f = I * g\\
g = \mu * f
\end{aligned}
\]
从之前的素因子式\(N=(\alpha_i)\)的角度理解,\((\alpha_i)\)其实是一个高维向量,而\(g(\alpha_i)\)就是这个向量的点权,\(f(\alpha_i)\)就是\(g\)的前缀和。联想到二维前缀和\(a_i = S_i - S_{i-1}\),莫比乌斯反演事实上就是对于高维前缀和\(f(\alpha_i)\)的差分变换。
实际上,我之前补充过一个冷知识:广义的莫比乌斯反演。对于偏序关系\(\preceq\),定义它的莫比乌斯函数:
\[
\mu(x,y) =
\begin{cases}
1 & x = y\\
-\sum_{x \preceq u \prec y} \mu(x,u) & x \prec y\\
0 & \text{else}
\end{cases}
\]
则有:
\[
\begin{aligned}
f(x) &= \sum_{0 \preceq u \preceq x} g(u)\\
g(x) &= \sum_{0 \preceq u \preceq x} \mu(u,x)f(u)
\end{aligned}
\]
这实际上说明了莫比乌斯反演实质上就是一个局部有限偏序集上的高维前缀和差分变换。这也进一步说明了莫比乌斯函数在容斥原理和数论上的链接作用。
模算术
正如你所见,\(a \mod b\)这个函数在设计时出现了一些问题,使得两个字母之间间隔太大。因此,下文中均用C++写法\(a\% b\)代表\(a\mod b\)。同理,为了方便书写,我们统一用\(a/b\)代表\(\lfloor\frac{a}{b}\rfloor\)。
欧拉定理
若正整数\(a,p\)互质,则:
\[
a^{\phi(p)} \equiv 1 \pmod{p}
\]
对于任意正整数\(b\),还有:
\[
a^b \equiv a^{b \% \phi(p)}\pmod{p}
\]
如果\(a\)和\(p\)不一定互质,则应该使用下面这个公式:
\[
a^{b} \equiv
\begin{cases}
a^{b\%{\phi(p)}+\phi(p)} & b > \phi(p)\\
a^b & b \leq \phi(p)
\end{cases}
\pmod{p}
\]
证明均略去。
裴蜀定理和exgcd
对于整数方程\(ax+by=c\),存在整数解\((x,y)\)的充要条件是\(\gcd(a,b)|c\)。它的另一个版本是\(ax+by = \gcd(a,b)\)一定有整数解。
它的证明是基于\(\gcd(a,b)=\gcd(b,a\%b)\)的。在递归终点\(b=0\)时,显然有\(x=1,y=0\)。否则,
根据数学归纳法,假设下面这个方程成立:
\[
bx+(a\%b)y = \gcd(b,a\%b)
\]
根据模数的另一种定义:
\[
a\%b = a - b(a/b)
\]
我们可以得到:
\[
bx+(a-b(a/b))y = \gcd(b,a\%b)=\gcd(a,b)
\]
整理得到:
\[
ay + b(x-(a/b)y) = \gcd(a,b)
\]
因此,我们令\(x'=y, y'=(x-(b/a)y)\),就可以得到 \(ax'+by'=\gcd(a,b)\)了。
在程序中可以这样简写:
inline ll exgcd(ll a, ll b, ll &x, ll &y)
{
if(b == 0)
{
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
一次同余方程FCE
一般形式为\(ax \equiv b \pmod{p}\)。可以转换成\(ax + py = b\)的形式,然后用exgcd解出\((x,y)\)。
假设\(\gcd(a,p)\not | b\),那么方程无解。
注意一下得到特解\(x_0\)后,方程的通解\(x \equiv x_0 \pmod{\frac{p}{\gcd(a,p)}}\)。
逆元
\(a\)在模\(p\)意义下的逆元为\(a^{-1}\),则:\(a\cdot a^{-1} \equiv 1 \pmod{p}\)。这是一个FCE,直接求解就可以了。
值得注意的是逆元的几种非常特殊的求法:
如果\(p\)是质数,那么\(a\)的逆元就是\(a^{p-2}\pmod{p}\)。
线性求逆元:\(i^{-1} \equiv (p-p/i)\times (p \% i) \pmod{p}\)
线性求阶乘逆元:\(\frac{1}{(i+1)!} \times (i+1) \equiv \frac{1}{i!} \pmod{p}\)
稍微注意一下,在用阶乘求逆元时可能会出现\(p | i!\)的情况,此时阶乘算出来都是\(0\)。
中国剩余定理
设方程组:
\[
\begin{cases}
x \equiv a_1 \pmod{p_1}\\
x \equiv a_2 \pmod{p_2}\\
\vdots\\
x \equiv a_k \pmod{p_k}
\end{cases}
\]
当\(p_1,p_2,\cdots,p_k\)两两互质时,设\(P=\prod_{i=1}^{k}p_i\),则方程一定有整数解:
\[
x \equiv \sum_{i = 1}^{k}a_i(\frac{P}{p_i})(\frac{P}{p_i})^{-1} \pmod{P}
\]
其中\((\frac{P}{p_i})^{-1}(\frac{P}{p_i})\equiv 1 \pmod{p_i}\)。
假设\(p_1,p_2,\cdots,p_k\)不满足两两互质,那么应该用扩展版本。
设\(x_{k-1}\)是通过EXCRT计算出的前\(k-1\)个方程的特解,而\(P_{k-1} = \prod_{i=1}^{k-1}p_i\)。为了让它满足第\(k\)个方程,有:
\[
x_{k-1} + \lambda P_{k-1} \equiv a_k \pmod{p_k}
\]
已知\(x_{k-1},P_{k-1},a_k,p_k\),这个方程就是一个关于\(\lambda\)的FCE,可以直接计算。当然,如果它无解,那整个方程组就都无解了。新的特解\(x_k=x_{k-1}+\lambda P_{k-1}\)通过加上或减去\(P_k\)就是前\(k\)个方程的特解。
另外有个小技巧:求乘积\(P_k\)可以改为求前\(k\)个模数的\(\operatorname{lcm}\)。粘贴核心代码:
int main()
{
n=qr(1);
RP(i,1,n)
m[i]=qr(1ll),a[i]=qr(1ll);//x = a[i] (mod m[i])
ans=0,M=1;//M = prod
RP(i,1,n)
{
ll p=FCE(M,a[i]-ans,m[i]);
ans=ans+p*M;
M*=m[i]/gcd(M,m[i]);
ans=((ans%M)+M)%M;
}
printf("%lld",((ans%M)+M)%M);
return 0;
}
高次同余方程(指数型)
求解形如\(a^{x}\equiv b \pmod{p}\)类型的,关于\(x\)的方程。其中有\(a,p\)互质,\(x\)非负。
这里没有一般的解析方法。但是由于\(a,p\)互质,我们可以自由计算逆元,进行和\(a\)有关的乘除法。
设\(x = \epsilon t - \eta\),其中\(t=\lceil\sqrt{p}\rceil\),\(0\leq \eta < t\)。事先声明,这样的记法其实有点“混用”的意味,但事实上相比普通的拉丁字母,它更有助于记忆理解。
方程变成了:
\[
a^{\epsilon t - \eta}\equiv b \pmod{p}
\]
即:
\[
a^{\epsilon t} \equiv b\cdot a^{\eta} \pmod{p}
\]
对于所有的\(b\cdot a^{\eta},0 \leq \eta < t\),我们将它插入一个Hash表中。枚举\(\epsilon\)的所有取值\(0,1,\cdots,t\),依次判断在Hash表中是否有对应的\(b\cdot a^{\eta}\),然后拼凑得到答案。
这种方法叫做Baby step,Giant step。是一种类分块的优化枚举。
原文地址:https://www.cnblogs.com/LinearODE/p/11564834.html
- 使用 R 语言从拉勾网看数据挖掘岗位现状
- 使用strace分析exp的奇怪问题(r3笔记第41天)
- Python文本挖掘:知乎网友如何评价《人民的名义》
- 怎样做中文文本的情感分析?
- 由一条日志警告所做的调优分析(r3笔记第40天)
- 生产环境sql语句调优实战第十篇(r3笔记第39天)
- memory_target设置不当导致数据库无法启动的问题(r3笔记第38天)
- python利用结巴分词做新闻地图
- 数据库静默安装总结(r3笔记第58天)
- 用TensorFlow实现文本分析模型,做个聊天机器人
- 深度学习:用tensorflow建立线性回归模型
- 用python基于2015-2016年的NBA常规赛及季后赛的统计数据分析
- 数值信息的机器级存储
- ABAP和Java里关于DEFAULT(默认)机制的一些语言特性
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【Java多线程-4】CompletionService详解
- 【Java多线程-5】 CompletableFuture详解
- 【Java多线程-6】synchronized同步锁
- 【Java多线程-7】阅尽Java千般锁
- 【SpringBoot注解-2】AOP相关注解详解
- 【SpringBoot注解-3】Bean注入相关注解
- 【SpringBoot注解-4】:@Target、@Retention、@Documented注解简介
- 【Linux系列-1】top命令详解
- 【Linux系列-2】iostat命令详解
- 【Mybatis-1】MyBatis注解版详解
- 【MyBatis-2】MyBatis之xml 配置版
- 【MyBatis-3】MyBatis xml映射文件详解
- 【剑指Offer】II-数组中数字出现的次数 II
- 【MyBatis-4】MyBatis之动态SQL
- 【MyBatis-4】MyBatis批量insert、update、delete数据