简单数论初步及一些筛法

简单数论初步

扩展同余方程欧几里得算法&一阶线性同余方程

对于一阶线性同余方程 a x ≡ c ( m o d b ) ax\equiv c\pmod{b} axc(modb)可以变形为不定方程 a x + b y = c ax+by=c ax+by=c,当 gcd ⁡ ( a , b ) ∣ c \gcd(a,b)\mid c gcd(a,b)c时,该方程有解。

对于一般的一阶线性同余方程我们是无法直接求解的,但可以通过拓展欧几里得算法求出通解。

d = gcd ⁡ ( a , b ) d=\gcd(a,b) d=gcd(a,b),则可以通过欧几里得先算出 a x + b y = d ax+by=d ax+by=d的通解,再转换成我们所需要的方程的解。

由欧几里得可以知, gcd ⁡ ( a , b ) = gcd ⁡ ( b , a % b ) \gcd(a,b)=\gcd(b,a\%b) gcd(a,b)=gcd(b,a%b),而 a % b = a − ( a / b ) × b a\%b=a-(a/b)\times b a%b=a(a/b)×b,假设方程的一组解为 x 1 , y 1 x_1,y_1 x1,y1,所以就有 d = gcd ⁡ ( a , b ) = gcd ⁡ ( b , a % b ) = gcd ⁡ ( b , a − ( a / b ) × b ) = b x 1 + ( a − ( a / b ) × b ) y 1 = a y 1 + b ( x 1 − a / b y 1 ) d=\gcd(a,b)=\gcd(b,a\%b)=\gcd(b,a-(a/b)\times b)=bx_1+(a-(a/b)\times b)y_1=ay_1+b(x_1-a/by_1) d=gcd(a,b)=gcd(b,a%b)=gcd(b,a(a/b)×b)=bx1+(a(a/b)×b)y1=ay1+b(x1a/by1)

就可以得出 x = y 1 , y = x 1 − a / b y 1 x=y_1,y=x_1-a/by_1 x=y1,y=x1a/by1。由拓展欧几里得不断递归,就可以得出 a x + b y = d ax+by=d ax+by=d的一组解 ( x 0 , y 0 ) (x_0,y_0) (x0,y0),进而可以求出其通解:
{ x = x 0 + b d × k y = y 0 − a d × k \begin{cases} x=x_0+\frac{b}{d} \times k\\y=y_0-\frac{a}{d} \times k\end{cases} {x=x0+db×ky=y0da×k
代码如下:

int exgcd(int a,int b,int& x, int& y)
{
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	int g = exgcd(b, a % b, y, x);
	int temp = x;
	x = y;
	y = temp - a / b * y;
	return g;
}

当然为了减少码量,可以稍微优化一下

int exgcd(int a,int b,int& x, int& y)
{
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	int g = exgcd(b, a % b, y, x);	//不借助中间变量,直接交换 x,y 的值,最终的x即为原来的y,而y也同理
	y = y - a / b * x;
	return g;
}

因为 a x 0 + b y 0 = d ax_0+by_0=d ax0+by0=d,两边同时乘以 c / d c/d c/d,可以得到

a ∗ ( c / d ∗ x 0 ) + b ∗ ( c / d ∗ y 0 ) = c a*(c/d*x_0)+b*(c/d*y_0)=c a(c/dx0)+b(c/dy0)=c

因而 x 1 = c / d ∗ x 0 , y 1 = c / d ∗ y 0 x_1=c/d*x_0,y_1=c/d*y_0 x1=c/dx0,y1=c/dy0是方程的一组解,

所以方程 a x + b y = c ax+by=c ax+by=c的通解为

{ x = x 1 + b d × k y = y 1 − a d × k \begin{cases} x=x_1+\frac{b}{d} \times k\\y=y_1-\frac{a}{d} \times k\end{cases} {x=x1+db×ky=y1da×k

要求x的最小正整数解的话,任取一个x值对 b / d b/d b/d取模即可,但是考虑到会存在x为负数的情况,可以用下面的方法

X = ( x % m + m ) % m X=(x\%m+m)\%m X=(x%m+m)%m (其中 m = b / d m=b/d m=b/d),来保证得出的结果一定是最小正整数。

同理,若要求不定方程 a x + b y = c ax+by=c ax+by=c y y y的解也同理。

中国剩余定理 ( C R T ) (CRT) (CRT)

对于 n n n 个关于 x x x 的同余方程
x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) ⋮ x ≡ a n ( m o d m n ) x \equiv {a_1}_\pmod {m_1} \\ x \equiv {a_2}_\pmod {m_2} \\ \vdots \\ x \equiv {a_n}_\pmod {m_n} xa1(modm1)xa2(modm2)xan(modmn)
(严格保证任意 m i , m j m_i, m_j mi,mj 互素)

M = Π i = 1 n m i M i = M / m i 记 t i = M i − 1 M=\Pi_{i=1}^nm_i\quad M_i=M/m_i \quad 记t_i=M_i^{-1} M=Πi=1nmiMi=M/miti=Mi1​ ,即表示为 M i M_i Mi​ 模 m i m_i mi​ 意义下的倒数,则 M i t i ≡ 1 ( m o d m i ) M_it_i\equiv 1_\pmod {m_i} Miti1(modmi)

可以得到方程组的一个特解: x = a 1 M 1 t 1 + a 2 M 2 t 2 + ⋯ + a n M n t n x=a_1 M_1 t_1+a_2 M_2 t_2 + \dots + a_n M_n t_n x=a1M1t1+a2M2t2++anMntn

联想前面的 e x g c d exgcd exgcd ,可以得知任意两个解的差的绝对值的最小值为

T = l c m { m 1 d 1 , m 2 d 2 , … , m n d n , } = l c m { m 1 , m 2 , … , m n } = M T=\mathrm { lcm } \{\dfrac{m_1}{d_1},\dfrac{m_2}{d_2},\dots,\dfrac{m_n}{d_n}, \}=\mathrm{lcm} \{m_1,m_2,\dots,m_n\}=M T=lcm{d1m1,d2m2,,dnmn,}=lcm{m1,m2,,mn}=M

(其中 d i d_i di 为第 i i i 个方程变形成不定方程后的两个未知数的最大公约数)

所以 x x x 的通解为: x = a 1 M 1 t 1 + a 2 M 2 t 2 + ⋯ + a n M n t n + k M x=a_1 M_1 t_1+a_2 M_2 t_2 + \dots + a_n M_n t_n+k M x=a1M1t1+a2M2t2++anMntn+kM

#include <bits/stdc++.h>

using namespace std;

//a[]表示余数,m[]表示模数
void extendEuclid(int a, int b, int& x, int& y)
{
    if (b == 0)
    {
        x = 1; y = 0;
        return;
    }
    extendEuclid(b, a % b, y, x);
    y -= a / b * x;
}

int ChineseRemainder(int a[], int m[], int n)
{
    int x, y, mi, M = 1, ans = 0;
    for (int i = 0; i < n; i++)
        M *= m[i];
  
    for (int i = 0; i < n; i++)
    {
        mi = M / m[i];
        extendEuclid(m[i], mi, x, y);
        ans = (ans + y * mi * a[i]) % M;
    }
    return (M + ans % M) % M;
}
int main()
{
    int n, i;
    int a[15], m[15];
    while (cin >> n && n)
    {
        for (i = 0; i < n; i++)
            cin >> a[i] >> m[i];
        cout << ChineseRemainder(a, m, n) << endl;
    }
    return 0;
}

扩展中国剩余定理 ( E X _ C R T ) (EX\_CRT) (EX_CRT)

基本问题背景与 C R T CRT CRT 一致,主要是不再保证任意 m i , m j m_i, m_j mi,mj​​ 互素。

方法其实与前者不太一样。

模板:

ll mul(ll a, ll b, ll mod)
{
	ll res = 0;
	while (b > 0)
	{
		if (b & 1) res = (res + a) % mod;
		a = (a + a) % mod;
		b >>= 1;
	}
	return res;
}
ll gcd(ll a, ll b)
{
	if (b == 0)
		return a;
	return gcd(b, a % b);
}
ll lcm(ll a, ll b, ll g)
{
	return a / g * b;
}
ll exgcd(ll a, ll b, ll& x, ll& y)
{
	if (b == 0) 
	{ 
		x = 1, y = 0;
		return a; 
	}
	ll gcd = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return gcd;
}
ll excrt()
{
	ll x, y, k;
	ll M = a[1], ans = m[1];
	for (int i = 2; i <= n; i++)
	{
		ll p = M, q = a[i], c = ((m[i] - ans) % q + q) % q;
		ll gcd = exgcd(p, q, x, y), mod = q / gcd;
		if (c % gcd != 0) return -1; 

		x = mul(x, c / gcd, mod);
		ans += x * M;
		M *= mod;
		ans = (ans % M + M) % M;
	}
	return (ans % M + M) % M;
}
a[N] m[N] 意义与前者一致

素数筛法及应用

1.埃氏筛法(貌似 n n n 1 e 6 1e^6 1e6 以内比线性筛更快?)

int vis[N], prime[N];

int is_prime()
{
	int index = 0;
	for (int i = 0; i <= N; i++)
		vis[i] = 1;
	vis[1] = 0;
	vis[0] = 0;
	for (int i = 2; i <= N; i++)
	{
		if (vis[i])
		{
			prime[index++] = i;
			for (int j = 2 * i; j <= N; j += i)
				vis[j] = 0;
		}
	}
	return index;					
}

2.线性筛 ( 貌似还挺有用的亚子)

其实更多的是用来求积性函数

int is_prime()
{
	int index = 0;
	memset(vis, 0, sizeof(vis));
	for (int i = 2; i <= N; i++)
	{
		if (!vis[i])
			prime[index++] = i;
		for (int j = 0; j < index && i * prime[j] <= N; j++)
		{
			vis[i * prime[j]] = prime[j]; //通过prime[j]直接写出每个合数的最小质因数,同时也可进行质因数分解
			if (i % prime[j] == 0)
				break;
		}
	}
	return index;					
}

3.区间素数筛法

利用类似埃氏筛法的原理,筛出前 b \sqrt b b 的素数即可,再对每一个素数划去它的倍数,同时也划去 [ a , b ) [a,b) [a,b) 区间内的倍数

剩下的就是素数了

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

typedef long long ll;

ll prime1[N], prime2[N];

ll a, b;

void is_prime(ll a, ll b)
{
	memset(prime1, 0, sizeof(prime1));
	memset(prime2, 0, sizeof(prime2));

	for (ll i = 2; i * i < b; i++)
	{
		if (prime1[i] == 0)
		{
			for (ll j = i; j * j < b; j += i)
				prime1[j] = 1;
			for (ll j = max(2LL, (a + i - 1) / i) * i; j < b; j += i)
            //(a + i - 1) / i) * i是第一个比a大的i的倍数
					prime2[j -a] = 1;
		}
	}
}

int main()
{
	while (cin >> a >> b)
	{
		int cnt = 0;
		is_prime(a, b);
		for (ll i = 0; i < b - a; i++)
			if (prime2[i] == 0)
				++cnt;
		if (a == 1)	cnt--;				//如果a=1,也会把1算上,所以要减去
		cout << cnt << endl;
	}
	return 0;
}

4.以及由线性筛(欧拉筛)拓展出来的对积性函数的筛法

1’ 欧拉函数

原理:

借助欧拉函数的性质对于任意一个数的欧拉函数 φ ( x ) \varphi(x) φ(x) x = n p x=np x=np ( p p p 为质数)当 p ∣ n p|n pn 时, φ ( x ) = p φ ( n ) \varphi(x)=p\varphi(n) φ(x)=pφ(n) ,否则 φ ( x ) = φ ( n ) φ ( p ) \varphi(x)=\varphi(n)\varphi(p) φ(x)=φ(n)φ(p) 恰好符合欧拉筛的过程。

code

int vis[maxn], phi[maxn], prime[maxn];

void eular()
{
    int index= 0;
    memset(phi, 0, sizeof(0));
    phi[1] = 1;
    for (int i = 2; i <= N; i++) 
    {
        if (!vis[i]) {
            prime[index++] = i;
            phi[i] = i - 1;
        }
        for (int j = 0; j < index && i * prime[j] <= N; j++) 
        {
            vis[prime[j] * i] = 1;
            if (i % prime[j] == 0) 
            {
                phi[prime[j] * i] = phi[i] * prime[j];
                break;
            }
            else phi[prime[j] * i] = phi[i] * phi[prime[j]];
        }
    }
}

2‘莫比乌斯函数

由定义
μ ( x ) = { 1 n = 1 ( − 1 ) k n = p 1 p 2 … p k 0 o t h e r w i s e \mu(x)=\begin{cases}1 & n=1\\ (-1)^k & n=p_1p_2 \dots p_k \\ 0 & otherwise \end{cases} μ(x)=1(1)k0n=1n=p1p2pkotherwise
即:只有当一个数 n ( n > 1 ) n(n>1) n(n>1) 的所有质因数次幂均为1时,才是非零数,借助欧拉筛可以判断一个数是否存在某个质因数的次幂超过1。

通过 if(i % p == 0) 即可判断

code

int mu[N], vis[N], prime[N];

voidbius()
{
    int index = 0;
    mu[1] = 1;
    for (int i = 2; i <= N; i++)
    {
        if (!vis[i])
        {
            prime[index++] = i;
            mu[i] = -1;
        }
        for (int j = 0; j < index && i * prime[j] < N; j++)
        {
            vis[i * prime[j]] = i;
            if (i % prime[j] == 0)
                break;
            mu[i * prime[j]] = -mu[i];
            //实际上表达式应该为:    mu[i * prime[j]] = mu[i] * mu[prime[j]];
        }
    }
}

2‘ 积性函数

已知函数 f ( x ) f(x) f(x) 满足 f ( i j ) = f ( i ) ∗ f ( j ) f(ij)=f(i)*f(j) f(ij)=f(i)f(j) 且满足当 gcd ⁡ ( i , j ) = 1 \gcd(i,j)=1 gcd(i,j)=1 时成立,则为积性函数

可以通过欧拉筛求值

数论零碎知识点

1’欧拉降幂

知道公式,求出了欧拉函数值就可以用了

A x % p = A x % φ ( p ) + φ ( p ) % p A^x\%p=A^{x\%\varphi(p)+\varphi(p)}\%p Ax%p=Ax%φ(p)+φ(p)%p

可见是对欧拉函数的一种应用,这不是废话吗

2‘乘法逆元

定义:如果有 a x ≡ 1 ( m o d n ) ax\equiv 1_\pmod n ax1(modn) ,则 x x x m o d   n \mathrm{mod}\,n modn 意义下 a a a 的乘法逆元,记作 x = i n v ( a ) x=\mathrm{inv} (a) x=inv(a) x = a − 1 x=a^{-1} x=a1

一个数有逆元当且仅当 gcd ⁡ ( a , n ) = 1 \gcd(a,n)=1 gcd(a,n)=1 且一个数的逆元唯一。

三种求法

1"利用扩展欧几里得:将 a x ≡ 1 ( m o d n ) ax\equiv 1_\pmod n ax1(modn) 展开得 a x + n y = 1 ax+ny=1 ax+ny=1 ,若 gcd ⁡ ( a , n ) = 1 \gcd(a,n)=1 gcd(a,n)=1 ,则可以通过 e x g c d exgcd exgcd 解出 x x x ,并调整到正常范围内。

2"利用费马小定理: 已知 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1_\pmod p ap11(modp) ,则 a ∗ a p − 2 ≡ 1 ( m o d p ) a*a^{p-2} \equiv 1_\pmod p aap21(modp)

p p p 为质数时可以考虑费马小定理

3"欧拉定理:已知 a φ ( p ) ≡ 1 ( m o d p ) a^{\varphi(p)} \equiv 1_\pmod p aφ(p)1(modp) ,可以得到逆元为 a φ ( p ) − 1 a^{\varphi(p)-1} aφ(p)1

适用于模数不是质数的情况

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值