数论
根号算法
bool isprime(int x)
{
if(x==0||x==1) return 0;
if(x==2) return 1;
for(int i=3;i<=sqrt(x);i++)
{
if(x%i==0) return 0;
}
return 1;
}
- 单次时间复杂度:\(O(\sqrt x)\)
- 判断1-n的素数个数:\(O(x\sqrt x)\)
埃氏筛法
- 先将2到n范围内的整数列出来,其中2是最小的素数。
- 将表中所有的2的倍数划去,表中剩下的最小的数字就是3,他不能被更小的数整除,所以3是素数。
- 再将表中所有的3的倍数划去……以此类推;
- 如果表中剩余的最小的数是m,那么m就是素数。
- 然后将表中所有m的倍数划去,像这样反复操作,就能依次枚举n以内的素数。
2 |
3 |
4 |
5 |
... |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
2 |
3 |
- |
5 |
... |
13 |
- |
15 |
- |
17 |
- |
19 |
- |
2 |
3 |
- |
5 |
... |
13 |
- |
- |
- |
17 |
- |
19 |
- |
例:给定一个正整数n(n<=10^6),问n以内有多少个素数?
int prime[MAXN];//第i个素数
bool is_pri[MAXN+10];//is_pri[i]表示i是素数
//返回n以内素数的个数
int sieve(int n)
{
int p=0;
for(int i=0;i<=n;i++) is_pri[i]=true;
is_pri[0]=is_pri[1]=false;
for(int i=2; i<=n; i++)
{
if(is_pri[i])
{
prime[++p]=i;
for(int j=2*i; j<=n; j+=i) is_pri[j]=false;
}
}
return p;
}
区间素数筛:给定两个正整数\(a,b(a<b\leq 10^{12},b-a\leq 10^6)\),请问[a,b)内有多少个素数?
#include<iostream>
using namespace std;
bool pri[1000000+10];
bool ispri[10000000+10];//ispri[i-a]=true代表i是素数
void getpri()
{
memset(pri,true,sizeof(pri));
pri[0]=pri[1]=0;
for(int i=2;i<=1000000;i++)
{
if(pri[i]){
for(int j=2*i;j<=1000000;j+=i)pri[j]=0;
}
}
}
int main(){
long long a,b;
scanf("%lld%lld",&a,&b);
getpri();
memset(ispri,true,sizeof(ispri));
for(long long i=2;i*i<b;i++)
{
if(pri[i])
{
for(long long j=max((a+i-1)/i,2LL)*i;j<b;j+=i)
ispri[j-a]=0;
}
}
long long cnt=0;
for(int i=0;i<b-a;i++)if(ispri[i])cnt++;
if(a==1)cnt--;
printf("%lld\n",cnt);
}
欧拉
欧拉筛
- 欧拉筛法的基本思想 :在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的。
int prime[maxn];
int visit[maxn];
void Prime()
{
mem(visit,0);
mem(prime, 0);
for (int i = 2;i <= maxn; i++)
{
cout<<" i = "<<i<<endl;
if (!visit[i])
{
prime[++prime[0]]=i;//纪录素数,这个prime[0]相当于cnt,用来计数
}
for (int j = 1; j <=prime[0] && i*prime[j] <= maxn; j++)
{
visit[i*prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
- 上面有一个
visit[i*prime[j]] = 1;
,这里解释一下
- 这里不是用i的倍数来消去合数,而是把prime里面纪录的素数,升序来当做要消去合数的最小素因子。
- i是在消去合数中的作用是当做倍数的。
- 还有一个
i % prime[j] == 0
- 当 i是prime[j]的倍数时,i=kprime[j],如果继续运算j+1,i * prime[j+1]=prime[j] * kprime[j+1],这里prime[j]是最小的素因子,当i=k * prime[j+1]时会重复,所以才跳出循环。
- 举个例子 i=8,j=1,prime[j]=2,如果不跳出循环,prime[j+1]=3,83=243=212,在i=12时会计算。因为欧拉筛法的原理便是通过最小素因子来消除。
- 附上欧拉筛代码:
/*求小于等于n的素数的个数*/
#include<stdio.h>
#include<string.h>
using namespace std;
int main()
{
int n, cnt = 0;
int prime[100001];//存素数
bool vis[100001];//保证不做素数的倍数
scanf("%d", &n);
memset(vis, false, sizeof(vis));//初始化
memset(prime, 0, sizeof(prime));
for(int i = 2; i <= n; i++)
{
if(!vis[i])//不是目前找到的素数的倍数
prime[cnt++] = i;//找到素数~
for(int j = 0; j<cnt && i*prime[j]<=n; j++)
{
vis[i*prime[j]] = true;//找到的素数的倍数不访问
if(i % prime[j] == 0) break;//关键!!!!
}
}
printf("%d\n", cnt);
return 0;
}
欧拉函数
- 欧拉函数\(φ(x)\)的值为在[1,x)的区间内与x互质的数的个数
- 通式:\(φ(x)=\prod_{i=1}^xn(1−\frac{1}{p_i})其中p_1,p_2,...,p_n\)为x的所有质因数,x是不为0的整数。\(φ(1)=1\)。
- 注意:每种质因数只一个。比如\(12=2×2×3\)那么\(φ(12)=12×(1−\frac{1}{2})×(1−\frac {1}{3})=4\)
- 介绍几个性质:
- 若n是质数p的k次幂,则\(φ(x)=p^k−p^{k−1}=(p−1)p^{k−1}\),因为除了p的倍数外,其他数都跟n互质。
- 积性函数——若m,n互质,\(φ(mn)=φ(m)φ(n)\)。
- 当n为质数时,\(φ(2x)=φ(x)\),其实与上述类似。
- 若n为质数则\(φ(x)=x−1\),这个挺重要的。
- 一个数的所有质因子之和S是\(S=\frac {nφ(n)}{2}\)。
//用通式算的
int euler(int n){ //返回euler(n)
int res=n,a=n;
for(int i=2;i*i<=a;i++){
if(a%i==0){
res=res/i*(i-1);//先进行除法是为了防止中间数据的溢出
while(a%i==0) a/=i;
}
}
if(a>1) res=res/a*(a-1);
return res;
}
//筛选法打欧拉函数表
#define Max 1000001
int euler[Max];
void Init()
{
euler[1]=1;
for(int i=2;i<Max;i++) euler[i]=i;
for(int i=2;i<Max;i++) if(euler[i]==i)
for(int j=i;j<Max;j+=i)
euler[j]=euler[j]/i*(i-1);//先进行除法是为了防止中间数据的溢出
}
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100000
int primes[N], euler[N], cnt;
bool st[N];
/* 欧拉函数
可以在 O(n) 的时间复杂度内求出 1~n中所有数的欧拉函数 同时也能算质数
质数存在primes[]中(用cnt统计<=n的素数个数),euler[i] 表示 i的欧拉函数*/
int get_eulers(int n)
{
euler[1] = 1;
for (int i = 2; i <= n; i ++ )
{
if (!st[i])
{
primes[cnt ++ ] = i;
euler[i] = i - 1;
}
for (int j = 0; j < cnt && i * primes[j] <= n; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0)
{
euler[i * primes[j]] = euler[i] * primes[j];
break;
}
euler[i * primes[j]] = euler[i] * (primes[j] - 1);
}
}
return cnt;
}
int main(void)
{
get_eulers(101);
for (int i = 0 ; i < cnt; i++)
{
cout << i << " " << primes[i] << endl;
}
cout << endl;
// 输出1到100每个数的欧拉函数值
for (int j = 1 ; j < 101; j++)
{
cout << j << " " << euler[j] << endl;
}
fclose(stdin);
fclose(stdout);
return 0;
}