数论
本文会记录基本数论知识以及相应的代码
整除
整除的定义:设 a,b \in Z(整数) ,a\neq 0。如果 \exists q \in Z, 使得 b=aq,那么就说 b 可被a 整除,记作a | b ,且称 b是a 的倍数,a 是b 的约数(因数)。
b 不被a 整除记作 。
素数
一、素数的判定
1.暴力做法
很容易发现这样一个事实:如果 x 是 a 的约数,那么 \frac{a}{x} 也是a 的约数。
这个结论告诉我们,对于每一对 (x,\frac{a}{x})只需要检验其中的一个就好了。为了方便起见,我们之考察每一对里面小的那个数。不难发现,所有这些较小数就是 [1,\sqrt{a}]这个区间里的数。
bool isprime(int a)
{
if(a<2) return false;
for(int i = 2; i <= a/i; i++){ //(1)
if(a%i==0) return false;
}
return true;
}
【代码说明】:(1)处,我们使用i<=a/i 而不使用i*i<=a 因为 i*i是有可能爆int的
2.素性测试
素性测试(Primality test)是一类在 不对给定数字进行素数分解(prime factorization)的情况下,测试其是否为素数的算法。
这部分内容,现在可以不学,22.3.13用不到
二、算数基本定理(唯一分解定理)
算术基本定理可表述为:任何一个大于1的
N=p_1^{a_1}\times p_2^{a_2}\times ...\times p_n^{a_n};
$$
其中p_1<p_2<...<p_n\ 且均为素数,其中所有指数a_i为正整数。
有了(1)式之后,我们可以求出 N的约数个数 以及 N的所有约数之和
约数个数:\prod_{i=1}^{n}(a_i+1)
约数之和:\prod_{i=1}^{n}(\sum_{j=0}^{a_i}p_i^j) = (p_1^0+p_1^1+..+p_1^{a_1})\times ...\times (p_n^0+p_n^1+..+p_n^{a_n})
程序实现分解质因数
求出 X=p_1^{a_1}\times p_2^{a_2}\times ...\times p_k^{a_k} 中的所有 p 以及对应的 a 。
-
试除法分解质因数
int p[N],a[N]; //分别存放pi,ai
int k=0;
void divide(int x)
{
for(int i = 2; i <= x/i; i++){
if(x % i==0){
int count = 0; //用于计算ai
while(x % i ==0) x/=i,s++;
p[k]=i;
a[k++] = count;
}
}
if(x>1) p[k]=x,a[k++]=1;
}
-
线性筛法分解质因数
先线性筛法求所有素数,再用求出素数分解处质因数
这种方法比较适用于多组测试数据时使用。
int allp[N],cnt=0; //这里意义同上
bool st[N]; //st[i]存储i是否被筛选过
//先得到所有的p,注意这里的n应为X所能取得最大值
void get_allp(int n)
{
for(int i = 2; i <= x; i++)
{
if(!st[i]) allp[cnt++] = i;
for(int j = 0; allp[j]<=n/i; j++)
{
st[allp[j]*i] = true;
if(i%p[j]==0) break;
}
}
}
int p[N],a[N],k=0;
void divide(int x)
{
for(int i = 0;allp[i]<=x;i++){
if(x%allp[i]==0)
{
p[k]=allp[i];
while(x%allp[i]==0) x/=allp[i],a[k]++;
}
}
}
最大公约数
最大公约数即为 Greatest Common Divisor,常缩写为 gcd。
一组整数的公约数,是指同时是这组数中每一个数的约数的数。 \pm1是任意一组整数的公约数。
一组整数的最大公约数,是指所有公约数里面最大的一个。
下面我们考虑两个数的情况,来求其最大公约数?
特别的:0和任意一个数的最大公约数都是其本身。即gcd(0,b)=b
欧几里得算法
又称为辗转相除法。
如果我们已知两个数 a和b ,如何求出二者的最大公约数呢?
不妨设 a>b
我们发现如果b 是a 的约数,那么 b就是二者的最大公约数。 下面讨论不能整除的情况,即a=b\times q+r ,其中 r<b。
我们通过证明可以得到 gcd(a,b)=gcd(b,a\mod\ b),过程如下:
https://oi-wiki.org/math/number-theory/gcd/
int gcd(int a, int b)
{
if(!b) return a;
return gcd(b, a % b);
}
欧几里得算法的时间复杂度为O(log n)
拓展欧几里得算法
扩展欧几里得算法(Extended Euclidean algorithm, EXGCD),常用于求 ax+by=gcd(a,b) 的一组可行解。其中x,y为整数。
下面程序实现求解方程ax+by=gcd(a,b) 的解 x,y 并且放回gcd(a,b)。
//递归实现,速度稍慢
int Exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1,y = 0;
return a;
}
int d = Exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - (a / b) * y;
return d;
}
//递归写法二
int exgcd(int a, int b, int &x, int &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y , x);
y -= a / b * x;
return d;
}
//迭代实现,速度会比递归快
int gcd(int a, int b, int& x, int& y) {
x = 1, y = 0;
int x1 = 0, y1 = 1, a1 = a, b1 = b;
while (b1) {
int q = a1 / b1;
tie(x, x1) = make_tuple(x1, x - q * x1);
tie(y, y1) = make_tuple(y1, y - q * y1);
tie(a1, b1) = make_tuple(b1, a1 - q * b1);
}
return a1;
}
数学证明过程如下:
设:
ax_1+by_1=gcd(a,b)
bx_2+(a \mod\ b)y_2=gcd(b,a \mod\ b)
由欧几里得定理可知:gcd(a,b)=gcd(b,a \mod\ b)
所以 ax_1+by_1=bx_2+(a \mod\ b)y_2
又因为 a \mod\ b=a-(\lfloor\frac{a}{b}\rfloor) 。。。。。。。。。。。
拓展欧几里得算法应用
应用有:\left\{ \begin{matrix} 求解不定方程\\ 求解模线性方程(线性同余方程)\\ 求解模的逆元 \end{matrix} \right.
$$
-
使用拓展欧几里得算法解决不定方程的办法
对于不定整数方程 ax+by=c,若\ c \mod\ gcd(a,b) =0,则该方程存在整数解,否则无整数解。
我们先求出方程ax+by=gcd(a,b) 的一组解 x_0 与 y_0,那么其他所有的整数解:
$$
x=x_0+\frac{k*b}{gcd},\ \ y=y_0-\frac{k*a}{gcd} \ \ \ (k \in Z)
$$
而对于方程:ax+by=c 来说,我们只要将x 和y 同时乘以 \frac{c}{gcd} 即可。
所以原方程的一组解为
x_1=x_0*\frac{c}{gcd};\ \ \ y_1=y_0*\frac{c}{gcd}
$$
所以其他整数解为:
x=x_1+\frac{k*b}{gcd}, \ \ \ y=y_1\frac{k*a}{gcd} \ \ \ (k \in Z)
$$
(5)即为方程的所有整数解。
-
-
欧几里得算法求模的逆元
同余方程ax≡b (mod n),如果 gcd(a,n)== 1,则方程只有唯一解。
当 b=1 时,同余方程求出的 x 为 a的对模n 乘法的逆元。 即解方程ax+ny=1\ \ (x,y \in Z)。
不常用:辗转相减法
利用
gcd(a,b)=gcd(a,b-a)来求出 gcd(a,b)。
$$
int gcd_sub(int a, int b)
{
if(a == b) return a;
if(a > b) a -= b;
else b -= a;
return gcd_s(a, b);
}
题目【辅导课--数论--5-1223.最大比例】
变形的相减法:
int gcd_sub(int a, int b)
{
if(a < b) swap(a, b);
if(b == 1) return a;
return gcd_sub(b, a / b);
}
浙公网安备 33010602011771号