一、数论初步
1)、欧几里德算法及其扩展
欧几里德算法也叫辗转相除法。根据gcd(a,b)=gcd(b,a%b)可以由递归写出该算法。
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
2)、素数相关
可用Eratosthenes筛法构造素数表。
void get_prime(int n);
{
memset(visit,0,sizeof(visit));
for(int i=2;i<=n;i++)
for(int j=i*2;j<=n;j+=1) visit[j]=1;
}
当然,这只是标记visit数组里的第i个元素是不是素数,要直接用取素数还是有点麻烦。可以用下面的方法来的得到素数。
void get_prime(int n)
{
memset(prime,0,sizeof(prime));
memset(flag,0,sizeof(flag));
int cnt=0;
for(int i=2;i<=n;i++)
{
if(flag[i]) continue;
prime[cnt++]=i;
for(int j=2;j<=n;j++) flag[i*j]=1;//if n=i*j,so n is not a prime!
}
}
但是这里要开一个flag的数组,占内存。可以不用开flag这个数组的。
void get_prime(int n)
{
memset(prime,0,sizeof(prime));
cnt=0;
for(int i=2;i<=n;i++)// for each number i,judge if i is a prime!
{
int isprime=1;
for(int j=2;j<i;j++)// I had made a mistake here,this is j<i,notj<=i!
{
if(i%j==0)
{
isprime=0;
break;
}
}
if(isprime) prime[cnt++]=i;
}
return;
}
下面是一道lrj白书上的一道例题,叫无平方因子的树。详见这里: UESCT 1720。
#include<stdio.h>
#include<string.h>
#define MAXN 1000005
int prime[MAXN],flag[MAXN];
int cnt=0;
long long n;
void get_prime()
{
memset(prime,0,sizeof(prime));
memset(flag,0,sizeof(flag));
for(int i=2;i<=MAXN;i++)
{
if(flag[i]) continue;
prime[cnt++]=i;
for(int j=2;j*i<MAXN;j++) flag[i*j]=1;
}
}
long long dfs(int s,long long num)
{
long long ans=0;
for(int i=s;i<cnt&&(long long)prime[i]*prime[i]<=num;i++)
ans+=num/prime[i]/prime[i]-dfs(i+1,num/prime[i]/prime[i]);
return ans;
}
int main()
{
int T;
get_prime();
scanf("%d",&T);
while(T--)
{
scanf("%lld",&n);
printf("%lld\n",n-dfs(0,n));
}
return 0;
}
3)、素数测试
用上面的方法可以可以判断一个数是不是素数,但是当素数太大是上面的方法就很费时,所以就要找一中简单有效的素数测试方法。
费马小定理:
如果a不是p的倍数,也可写为:
虽然满足上式的数不一定是素数,但是不满足的却一定是合数。因此,当判断一个素数是,我们就判断他是否满足上式。这样多测试几组数据,就可以很准确的判断这个数是不是素数了。这就是Miller-Rabin素数测试方法。
Miller-Rabin算法
to be continue!