质数与合数

质数

定义:在大于1的整数中,如果只包含1和本身两个约数,就被称为质数,或者称为素数。特别地,0,1既不是质数也不是合数。
质数的判定:

1. 质数判定——试除法

时间复杂度: O ( n ) O(\sqrt{n}) O(n )
i从2枚举到n-1,如果n能被i整除,则为合数;如果都不能被i整除,则为质数;
如果n为合数,因子成对出现,记为(x1,x2),则必有 x 1 < = n , x 2 > = n x1<=\sqrt{n},x2>=\sqrt{n} x1<=n ,x2>=n ,所以i枚举到 n \sqrt{n} n 即可

bool is_prime(int n)
{
	if(n<2) return false;
	else
	{
		for(int i=2;i<=n/i;i++)//i<sqrt(n) 每次会开方,计算量大  i*i<=n 有爆int风险
			if(n%i==0) return false;
	}
	return true;
}

2. 分解质因数——试除法

时间复杂度: O ( log ⁡ 2 n ) O(\log_2 n) O(log2n)~ O ( n ) O(\sqrt{n}) O(n )
n最多只有1个大于 n \sqrt{n} n 的质因数

void divide(int n)
{
	for(int i=2;i<n/i;i++)
	{
		if(n%i==0)
		{
			int s=0;
			while(n%i==0) n/=i,s++;
			cout<<i<<' '<<s<<endl;
		}
	}
	if(n>1) cout<<n<<' '<<1<<endl;
}

3. 筛质数(求1-n中质数的数量)

1. 埃氏筛法(朴素筛)

时间复杂度: O ( n l n n ) O(nlnn) O(nlnn)
对于每个i,内部循环次数 c n t t = n i cnt_t=\frac{n}{i} cntt=in
整个程序 c n t = ∑ i = 2 n c n t t = n 2 + n 3 + . . . + 1 = n ∑ i = 2 n 1 i ≈ n ( l n n + c ) cnt=\sum_{i=2}^{n}{cnt_t}=\frac{n}{2}+\frac{n}{3}+...+1=n\sum_{i=2}^{n}{\frac{1}{i}}\approx n(lnn+c) cnt=i=2ncntt=2n+3n+...+1=ni=2ni1n(lnn+c),c为欧拉常数

int prime[n],cnt=0;
bool st[n];
for(int i=2;i<n-1;i++)
{
	if(!st[i]) //2~i-1都没有将i筛掉,所以i是质数
	{
		prime[cnt++]=i;
		for(int j=i*2;j<=n;j+=i)
			st[j]=true;
	}
}

2. 线性筛法(欧拉筛)

1~n中大约有 n ln ⁡ n \frac{n}{\ln{n}} lnnn个质数,时间复杂度大约是O(n),实际是O(nloglog(n))

int prime[n],cnt=0;
bool st[n];
for(int i=2;i<=n;i++)
{
	if(!st[i]) prime[cnt++]=i;
	for(int j=0;prime[j]<=n/i;j++)// 保证prime[j]*i<=n
	{
		st[prime[j]*i]=true;
		// 如果i是质数,则prime[-1]=i  可以break
		// 如果i是合数,则prime[j]=i的第一个质因子的时候break
		if(i%prime[j]==0) break;
	}
}

核心:每个合数只由它最小的质因子筛除

  1. i为质数
    prime[j]从小到大遍历,prime[j]<i,prime[j]一定是prime[j]*i的最小质因子
  2. i为合数
    i = x ∗ y i=x*y i=xy(x为i的最小质因子)
    prime[j]从小到大遍历,当prime[j]<=x ,prime[j]一定是prime[j]*i的最小质因子
    当prime[j]>x,x是prime[j]*i的最小质因子,所以prime[j]一旦遍历到x就终止遍历
    例如:
    i = 8 i=8 i=8(8的最小质因子为2)
    16 = 2 ∗ 8 16=2*8 16=28由i=8和最小质因子2筛除
    24 = 3 ∗ 8 24=3*8 24=38因为8中有质因子2,所以应该由i=12和质因子2筛除,不应该由i=8和质因子3筛除,所以当i遇到它的第一个质因子时应当退出循环。

约数

1.试除法求约数

时间复杂度: O ( n ) O(\sqrt{n}) O(n )
i从1枚举到n,如果n能被i整除,则为i为n的一个约数;
因子成对出现,记为(x1,x2),则必有 x 1 < = n , x 2 > = n x1<=\sqrt{n},x2>=\sqrt{n} x1<=n ,x2>=n ,所以i枚举到 n \sqrt{n} n 即可

vector<int> get(int n)
{
    vector<int> res;
    for(int i=1;i<=n/i;i++)
    {
        if(n%i==0) 
        {
            res.push_back(i);
            if(i!=n/i) res.push_back(n/i);
        }
    }
    sort(res.begin(),res.end());
    
    return res;
}

2.约数个数

AcWing 870. 约数个数
将x能表示成它所有质数的乘积 x = p 1 α 1 p 2 α 2 . . . p k α k x=p_1^{\alpha1}p_2^{\alpha2}...p_k^{\alpha k} x=p1α1p2α2...pkαk
那么它的任意一个约数可以表示成 d = p 1 β 1 p 2 β 2 . . . p k β k d=p_1^{\beta1}p_2^{\beta2}...p_k^{\beta k} d=p1β1p2β2...pkβk, 0 ≤ β i ≤ α i 0\leq\beta i\leq\alpha i 0βiαi
所以n的约数总个数 n = ( α 1 + 1 ) ( α 2 + 1 ) . . . ( α k + 1 ) n=(\alpha1+1)(\alpha2+1)...(\alpha k+1) n=(α1+1)(α2+1)...(αk+1)

3.约数总和

AcWing 871. 约数之和
约数的个数中提到,数x的任意一个约数可以表示成 d = p 1 β 1 p 2 β 2 . . . p k β k d=p_1^{\beta1}p_2^{\beta2}...p_k^{\beta k} d=p1β1p2β2...pkβk, 0 ≤ β i ≤ α i 0\leq\beta i\leq\alpha i 0βiαi,约数可以表示如下:
d 1 = p 1 0 p 2 0 . . . p k 0 d1=p_1^0p_2^0...p_k^0 d1=p10p20...pk0
d 2 = p 1 0 p 2 0 . . . p k 1 d2=p_1^0p_2^0...p_k^1 d2=p10p20...pk1
d 3 = p 1 0 p 2 0 . . . p k 2 d3=p_1^0p_2^0...p_k^2 d3=p10p20...pk2
所有的约数就是 β 1 \beta1 β1~ β k \beta k βk所有取值的全排列,即 ( p 1 0 + p 1 1 + . . . + p 1 α 1 ) ( p 2 0 + p 2 1 + . . . + p 2 α 2 ) . . . ( p k 0 + p k 1 + . . . + p k α k ) (p_1^0+p_1^1+...+p_1^\alpha1)(p_2^0+p_2^1+...+p_2^\alpha2)...(p_k^0+p_k^1+...+p_k^{\alpha k}) (p10+p11+...+p1α1)(p20+p21+...+p2α2)...(pk0+pk1+...+pkαk)

4.最大公约数

a和b的最大公约数等于b和a%b的最大公约数

int get(int a,int b)
{
    return b?get(b,a%b):a;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值