素数与Miller-Rabin测试
素数及其性质
素数又称质数。指整数在一个大于 的自然数中,除了 和此整数自身外,没法被其他自然数整除的数。
性质1 有无穷多个素数。
证明: 用反证法。设已知的最大素数为 ,考虑
显然所有已知素数都无法整除 ,所以我们找到了一个更大的素数。
性质2 对于正整数 ,若 是素数且 ,则 互素。
证明: 由素数定义知, 的因子为 。
即 互素。
性质3 对于任意正整数 ,存在连续的 个合数。
证明: 考虑
这 个数都是合数。
性质4 若 是素数, 是小于 的正整数,则 。
证明: 详细证明戳这里。
题目描述
给定两个整数 ,求闭区间 中相邻两个质数差值最小的数对与差值最大的数对。当存在多个时,输出靠前的素数对。
输入格式
多组数据。每行两个数 。
输出格式
详见输出样例。
样例输入
2 17
14 17
样例输出
2,3 are closest, 7,11 are most distant.
There are no adjacent primes.
数据范围与提示
对于全部数据,,。
由于 的数值较小,自然想到把 间的素数筛出,然后找出答案。
对于 区间的数的质因子集 ,必有
先筛出 内的素数,再尝试从 的 倍开始筛去合数,剩下的就是素数。注意特判 。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
const int inf=1e6+10;
int L,R;
int a[1000010],b[1000010],d[1000010];
int len=0,c,set=0;
int l1,r1,l2,r2,ans1,ans2;
void work(){
memset(b,1,sizeof(b));
for(reg int i=2;i<=inf;++i){
if(b[i]){
b[i]=0;a[++len]=i;
for(reg int j=i+i;j<=inf;j+=i)
b[j]=0;
}
}
}
int main(){
work();
while(scanf("%d%d",&L,&R)!=EOF){
l1=r1=l2=r2=0;ans1=0x3f3f3f3f;ans2=-1;
memset(d,1,sizeof(d));
if(L==1) d[0]=0;
for(reg int i=1;i<=len;++i){
c=L/a[i];
//if(ll(a[i]*c)<(ll)L) ++c;
for(reg int j=c<=1?2:c;j<=R/a[i];++j){
int dd=j*a[i]-L;
if(dd>R-L||dd<0) continue;
d[dd]=0;
}
}
for(reg int i=0;i<=R-L;++i)
if(d[i]){
set=i;
break;
}
if(set==-1){
puts("There are no adjacent primes.");
continue;
}
for(reg int i=set+1;i<=R-L;++i)
if(d[i]){
if(i-set<ans1){
ans1=i-set;
l1=set+L;r1=i+L;
}
if(i-set>ans2){
ans2=i-set;
l2=set+L;r2=i+L;
}
set=i;
}
// for(reg int i=0;i<=R-L;++i)
// printf("%d ",d[i]);
// puts("");
if(!l1&&!r1&&!l2&&!r2)
puts("There are no adjacent primes.");
else
printf("%d,%d are closest, %d,%d are most distant.\n",l1,r1,l2,r2);
}
}
题目描述
原题来自:NOIP 2012 普及组
已知正整数 是两个不同的质数的乘积,试求出较大的那个质数。
输入格式
输入只有一行,包含一个正整数 。
输出格式
输出只有一行,包含一个正整数 ,即较大的那个质数。
样例输入
21
样例输出
7
数据范围与提示
对于 的数据, ;
对于全部数据,。
由于 是 个素数之积,所以当我们找到一个较小素数 使 时,另一个素因子就是 。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
ll n;
bool b[200000010];
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
void work(){
memset(b,1,sizeof(b));
for(reg ll i=2;i*i<=n;++i){
if(b[i]){
b[i]=0;
if(!(n%i)){
printf("%lld",max(i,n/i));
exit(0);
}
for(reg ll j=i+i;j<=min(n,20000000);j+=i)
b[j]=0;
}
}
}
int main(){
scanf("%lld",&n);
work();
}
题目描述
哥德巴赫猜想:任何大于 的偶数都可以拆成两个奇素数之和。
你的任务是:验证小于 的数满足哥德巴赫猜想。
输入格式
多组数据,每组数据一个 。
读入以 结束。
输出格式
对于每组数据,输出形如 ,其中 是奇素数。若有多组满足条件的 ,输出 最大的一组。
若无解,输出 Goldbach's conjecture is wrong.。
样例输入
8
20
42
0
样例输出
8 = 3 + 5
20 = 3 + 17
42 = 5 + 37
数据范围与提示
对于全部数据,。
- 预处理出 的素数;
- 对于每一个 ,枚举 判断是否合法。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
int n,s;
int a[100010],b[1000010];
int len=0;
int f[1000010];
void reset(){
memset(b,1,sizeof(b));
for(reg int i=2;i*i<=1000000;++i)
if(b[i]){
a[++len]=i;
for(reg int j=i+i;j<=1000000;j+=i)
b[j]=0;
}
}
void work(int x){
if(!x) return;
if(f[x]){
printf("%d = %d + %d\n",x,f[x],x-f[x]);
return;
}
for(reg int i=2;i<=len/2+1;++i)
if(b[x-a[i]]){
f[x]=a[i];
printf("%d = %d + %d\n",x,f[x],x-f[x]);
return;
}
}
int main(){
memset(f,0,sizeof(f));
reset();
do{
scanf("%d",&s);
work(s);
}while(s);
}
题目描述
Sherlock 有了一个新女友(这太不像他了!)。情人节到了,他想送给女友一些珠宝当做礼物。
他买了 件珠宝。第 件的价值是 。那就是说,珠宝的价值分别为 。
Watson 挑战 Sherlock,让他给这些珠宝染色,使得一件珠宝的价格是另一件的质因子时,两件珠宝的颜色不同。并且,Watson 要求他最小化颜色的使用数。
请帮助 Sherlock 完成这个简单的任务。
输入格式
只有一行一个整数 ,表示珠宝件数。
输出格式
第一行一个整数 ,表示最少的染色数;
第二行 个整数,表示第 到第 件珠宝被染成的颜色。若有多种答案,输出任意一种。
样例输入 1
3
样例输出 1
2
1 1 2
样例输入 2
4
样例输出 2
2
2 1 1 2
样例说明
因为 是 的一个质因子,因此第一件珠宝与第三件珠宝的颜色必须不同。
数据范围与提示
对于全部数据,。
易证,所有的素数都是互素的。
所以,我们可以把所有的素数全部染成一种颜色,把其他数染成另一种颜色。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
int n;
int a[100010];
void work(){
memset(a,1,sizeof(a));
for(reg int i=2;i<=n+5;++i)
if(a[i])
for(reg int j=i+i;j<=n+5;j+=i)
a[j]=0;
}
int main(){
scanf("%d",&n);
if(n==1){
puts("1\n1");
exit(0);
}
if(n==2){
puts("1\n1 1");
exit(0);
}
work();
puts("2");
for(reg int i=1;i<=n;++i)
printf("%d ",a[i+1]?1:2);
}
题目描述
对于Fibonacci数列: 大家应该很熟悉吧~~~但是现在有一个很“简单”问题:第 项和第 项的最大公约数是多少?
输入格式
两个正整数 和 。()
输出格式
和 的最大公约数。
由于看了大数字就头晕,所以只要输出最后的8位数字就可以了。
输入样例
4 7
输出样例
1
今有结论
证明: 当 时,结论显然成立。不妨设 ,记 。
则
而
。
引理:对于 ,有 。
证明:
我们发现,这实质就是求解 的过程,
于是问题转化成求 的值。使用矩阵乘法和快速幂完成求解。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define reg register
typedef long long ll;
int a,b;
int n,m;
struct node{
ll a[3][3];
int x,y;
node(){
x=y=0;
memset(a,0,sizeof(a));
}
void pt(){
printf("%lld",a[1][2]);
}
}s,t,ans;
node T(node a,node b){
node c;c.x=a.x;c.y=b.y;
for(reg int i=1;i<=c.x;++i)
for(reg int j=1;j<=c.y;++j)
for(reg int k=1;k<=a.y;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
return c;
}
node QT(node a,int b){
if(b<=1) return a;
node c=QT(a,b/2);
if(b%2)
return T(T(c,c),a);
else
return T(c,c);
}
int main(){
scanf("%d%d",&a,&b);
n=std::__gcd(a,b);m=100000000;
if(n<=2){
puts("1");
exit(0);
}
s.x=1;s.y=2;s.a[1][1]=s.a[1][2]=1;
t.x=2;t.y=2;t.a[1][1]=0;t.a[1][2]=t.a[2][1]=t.a[2][2]=1;
ans=T(s,QT(t,n-2));
ans.pt();
}
Miller-Rabin 测试
戳这儿进行预习。
Miller-Rabin 测试基于以下定理。
若 是素数, 是小于 的正整数,且 ,则 或 。
以 为例,Miller-Rabin 测试步骤如下:
- 计算得 ;
- 将 除以 ,得到 ;
- 计算得 ;
- 将 除以 ,得到 ;
- 计算得 ,所以 不是素数( 是伪素数)。
- * 若是素数,则换用 或其他素数重复以上操作。
一般地,我们把 有 的合数 叫做以 为底的 伪素数。

浙公网安备 33010602011771号