【説明する】素数
素数栗子:
素数相关知识:
素数概念:
最大公约数只有1和它本身的数叫做质数(素数)
素数小性质:
1.大于一的整数必有素因数。
2.设p是素数,n是任意一个整数
能够推出p|n,(p,n)=1;
3.设p是素数,a,b为整数,若p|ab,则ab中至少有一个能被p整除
4.素数有无穷多个证明:
(素数与整数之间的关系:1整除2互素)
假定正整数中只有有限个素数
设p1,p2……pk为从小到大排列起来的数
且N=p1*p2*……pk
设M=N+1
如果M为素数,那么M要大于p1,p2……pk,
所以它不在那些假设的素数集合中
若M为合数
∵任何一个合数都可以分解为几个素数的积,而N和M的最大公约数是1
∴p不可能被p1,p2……pk整除
∴该合数分解得到的素因数肯定不在假设的素数集合中。
∴无论M是素数还是合数,都意味着在假设的有限个素数之外还存在着其他素数。
∴原先的假设不成立,也就是说,素数有无穷多个。
5. 质数的个数公式 style='orphans: auto;text-align:start;widows: auto;-webkit-text-stroke-width: 0px; word-spacing:0px' align=absmiddle title="" v:shapes="_x0000_i1025"> 是不减函数
6. 若n为正整数,在 align=absmiddle title="" v:shapes="_x0000_i1026"> 到 align=absmiddle title="" v:shapes="_x0000_i1027"> 之间至少有一个质数
7. 若n为大于或等于2的正整数,在n到 之间至少有一个质数
8. 若质数p为不超过n( )的最大质数,则
9. 所有大于10的质数中,个位数只有1,3,7,9
至今为止,没有任何人发现素数的分布规律,也没有人能用一个公式计算出所有的素数。
关于素数的很多的有趣的性质或者科学家的努力
1.高斯猜测,n以内的素数个数大约与n/ln(n)相当,或者说,当n很大时,两者数量级相同。这就是著名的素数定理。
2.十七世纪费马猜测,2的2^n次方+1,n=0,1,2…时是素数,这样的数叫费马素数,可惜当n=5时,2^32+1就不是素数,
至今也没有找到第六个费马素数。
3.18世纪发现的最大素数是2^31-1,19世纪发现的最大素数是2^127-1,20世纪末人类已知的最大素数是2^859433-1,用十进制表示,这是一个258715位的数字。
4.孪生素数猜想:差为2的素数有无穷多对。目前知道的最大的孪生素数是1159142985×2^2304-1和1159142985×2^2304+1。
5. 歌德巴赫猜想:大于2的所有偶数均是两个素数的和,大于5的所有奇数均是三个素数之和。其中第二个猜想是第一个的自然推论,因此歌德巴赫猜想又被称为1+ 1问题。我国数学家陈景润证明了1+2,即所有大于2的偶数都是一个素数和只有两个素数因数的合数的和。国际上称为陈氏定理。
合数概念:
合数是除了1和它本身以外 还能被其他的正整数整除的正整数。
除2之外的偶数都是合数。(除0以外)
合数又名合成数,是满足以下任一(等价)条件的正整数,
性质 :
1.是两个大于 1 的整数之乘积;
2.拥有某大于 1 而小于自身的因数(因子);
3.拥有至少三个因数(因子);
4.不是 1 也不是素数(质数);
5.有至少一个素因子的非素数。
筛法求素数:
若m是合数,p是m的最小正约数,所以p<=sqrt(m);
证明:∵m为p的约数,
那么我们可以设 m=p*q;
又∵p是最小的正约数;
∴q>=p;//p不等于=q且p是最小的正约数;
∴p*p<p*q=m;
∴p<=sqrt(m);
在1到m之间,运用筛法求素数:(floor为向下取整)
即在1到m中,把2到floor(sqrt(m))中素数的倍数(倍数>1)和1都去掉,其余的数就是素数;
但是有一点要注意的是:
运用筛法,只能求1到m之间的素数,但是不能求n到m之间的素数
基本判断思路:
①在一般领域,对正整数n,如果用2到sqrt(n)之间的所有整数去除,均无法整除,则n为质数。
②质数大于等于2
③不能被它本身和1以外的数整除
给出代码:
我自己胡乱搞的方法:
int pd(int x)
{
if(x==2||x==3) return 1;
if(x%2==0 || x==1) return 0;
int j=3;
while(j<=sqrt(x)&&x%j!=0) j+=2;
if(x%j==0) return 0;
else return 1;
}
普通方法直接来判断是否为素数:
#include <iostream>
#include <cmath>
using namespace std;
int main() {
int b;
while(cin>>b) {
if(b<0) {
cout<<"It's can't a prime! Because it's tan 90 ^_^, Don't ask me why!"<<endl;
continue;
}
if(b<=1 && b>=0) {
cout<<"It's not a prime! Because it's rules!"<<endl;
continue;
}
bool ok=true;
for(int i=2; i<=sqrt(b); ++i) {
if(b%i==0) {
ok=false;
cout<<"It's not a prime, because of the smallest number "<<i<<endl;
break;
}
}
if(ok)
cout<<"Yes, this is a prime!"<<endl;
}
return 0;
}
经典的Eraosthenes筛法:
#include <iostream>
#include <cmath>
using namespace std;
const int Maxn = 10000001;
int n,cnt;
int prime[Maxn];
bool notprime[Maxn];
void Eraosthenes_pre() {
for(int i=2; i<sqrt(n); ++i) {
if(notprime[i])
continue;
for(int j=i; i*j<n; ++j)
notprime[i*j]=true;
}
for(int i=2; i<n; ++i)
if(!notprime[i])
prime[cnt++]=i;
}
int main() {
cin>>n;
Eraosthenes_pre();
for(int i=0; i<cnt; ++i)
cout<<prime[i]<<endl;
return 0;
}
但是Eraosthenes筛法的速度并不快,原因在于对于一个合数,这种方法会重复的标记。
一种线性筛素数的方法有效的解决了这一点。
线性筛代码如下:
void get_prime() {
notprime[0]=notprime[1]=true;
for(int i=2; i<=n; ++i) {
if(!notprime[i])
prime[++cnt]=i;
for(int j=1; j<=cnt && i*prime[j]<=n; ++j) {
notprime[i*prime[j]]=true;
if(i%prime[j]==0)
break;
}
}
}
小知识点:
唯一分解定理定义:
任意大于1的整数,若不计因子排列顺序,都可以被唯一分解为素数(不会是只有2个素数的乘积,可能是多个)的乘积;
n=p1α1×p2α2×……×psαs
pi(i=1,2,……,s)为两两互不相等的素数,
αi(i=1,2,……,s)为正整数;
举个栗子:56=2^3*7;
C++代码实现:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 10086
using namespace std;
bool ss[maxn];
int css[maxn],num;
int ans[maxn],times[maxn],cnt;
int n;
void zss()
{
memset(ss,1,sizeof(ss));
ss[1]=0;
for(int i=2;i<=maxn;i++)
if(ss[i])
{
css[++num]=i;
for(int j=2;j*i<=maxn;j++)ss[i*j]=0;
}
}
int main()
{
scanf("%d",&n);
printf("%d=",n);
zss();
int k=1;
while(n!=1&&k<=num)
{
int p=0;
if(n%css[k]==0)
{
while(n%css[k]==0)
{
n/=css[k];
p++;
}
ans[++cnt]=css[k];
times[cnt]=p;
}
k++;
}
for(int i=1;i<cnt;i++)
if(times[i]!=1) printf("%d^%d*",ans[i],times[i]);
else printf("%d*",ans[i]);
if(times[cnt]==1) printf("%d",ans[cnt]);
else printf("%d^%d",ans[cnt],times[cnt]);
return 0;
}
例题
例题1:质因数分解
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
已知正整数 n 是两个不同的质数的乘积,试求出较大的那个质数。
- 输入
- 输入只有一行,包含一个正整数 n。
对于60%的数据,6 ≤ n ≤ 1000。
对于100%的数据,6 ≤ n ≤ 2*10^9。 - 输出
- 输出只有一行,包含一个正整数 p,即较大的那个质数。
- 样例输入
-
21
- 样例输出
- 7
- 思路:
根据唯一分解定理,若此题有答案,则输入数据满足有且只有一组质数相乘=n
所以,i从2循环到根号n,如果n%i==0,则n/i为答案
也就是说,n=质数a*质数b,n没有其他的分解
证明:
假设还有另外一组分解c*d
那么c*d分解质因数的结果与a*b相同
又因为a、b是质数
所以a*b分解质因数=a*b
所以c=a,d=b
即只有一种分解
c++代码实现:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int pd(int x) {
if(x==2 || x==3) return 1;
if(x%2==0 || x==1) return 0;
int j=3;
while(j<=sqrt(x) && x%j!=0) j+=2;
if(x%j==0) return 0;
else return 1;
}
int main() {
int n;
scanf("%d",&n);
int t=sqrt(n); //i最大的范围
for(int i=2; i<=t; i++) { //因为1不是质数,所以循环从2开始进行
if(n%i==0) { //如果找到了能够进行整除的i
//又因为样例说一定满足n 是两个不同的质数的乘积,所以直接输出另外一个数就行
//if(pd(i)) {//所以由上得:不需要判断第一个数是否能够被模 ,即满足唯一分解定理
printf("%d",n/i);
return 0;
// }
}
}
return 0;
}
例题2:第n小质数
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
输入一个正整数n,求第n小的质数。
- 输入
- 一个不超过10000的正整数n。
- 输出
- 第n小的质数。
- 样例输入
-
10
- 样例输出
- 29
- 坑点:
- 一定要注意范围!!!!范围!!!!范围!!!!
- c++代码实现
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
/*
#define M 10001这里!!数组开的不能太小!!!
上面跟下面选取一个进行改正
*/
#define M 10002
using namespace std;
struct Q {
int top;
Q() { top=0; }
int s[M];
void jiajia() { top++; }
int add(int x) { s[top]=x; }
} q;
int pd(int x) {
if(x==2 || x==3) return 1;
if(x%2==0 || x==1) return 0;
int j=3;
while(j<=sqrt(x) && x%j!=0) j+=2;
if(x%j==0) return 0;
else return 1;
}
void Q_work() {
q.jiajia();
q.add(2);
for(int i=3;; i++) {
if(pd(i)) {
q.jiajia();
q.add(i);
}
if(q.top>10000) //因为结束条件是q.top>10000所以需要使用到10001个,所以数组需要开到10002
//if(q.top>=10000) 或者上面不改,改这里
break;
}
}
int main() {
int n;
scanf("%d",&n);
Q_work();
printf("%d",q.s[n]);
return 0;
}
例题3:1530 大质数
小明因为没做作业而被数学老师罚站,之后数学老师要他回家把第n个质数找出来。(1<=n<=100000)
老师随机写了几个数,交给了小明。小明百度找了很久,都没能解决。现在交给聪明的你。请你帮忙!
—————————————————————————————————————————————
简单描述:把第n个质数找出来。
一个正整数n。
(1<=n<=100000)
第n个质数。
(第1个质数为2,第2个质数为3。)
样例1
2
样例2
65
样例3
20133
样例1
3
样例2
313
样例3
226381
c++代码实现
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int pd(int x) {
if(x==2 || x==3) return 1;
if(x%2==0 || x==1) return 0;
int j=3;
while(j<=sqrt(x)&&x%j!=0) j+=2;
if(x%j==0) return 0;
else return 1;
}
int n,ans,js;
void Q_work() {
if(n==1) {
cout<<"2";
return;
}
js=1;
for(int i=2; js!=n; i++) {
if(pd(i)) {
ans=i;
js++;
}
}
printf("%d",ans);
}
int main() {
scanf("%d",&n);
Q_work();
return 0;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define M 100001
using namespace std;
struct Q {
int top;
Q() {
top=0;
}
int s[M];
void jiajia() {
top++;
}
int add(int x) {
s[top]=x;
}
} q;
int pd(int x) {
if(x==2 || x==3) return 1;
if(x%2==0 || x==1) return 0;
int j=3;
while(j<=sqrt(x)&&x%j!=0) j+=2;
if(x%j==0) return 0;
else return 1;
}
int n;
void Q_work() {
q.jiajia();
q.add(2);
for(int i=3;; i++) {
if(pd(i)) {
q.jiajia();
q.add(i);
}
if(q.top>=n)
break;
}
}
int main() {
scanf("%d",&n);
Q_work();
printf("%d",q.s[n]);
return 0;
}
例题4:判决素数个数
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
输入两个整数X和Y,输出两者之间的素数个数(包括X和Y)。
- 输入
- 两个整数X和Y(1 <= X,Y <= 105)。
- 输出
- 输出一个整数,表示X,Y之间的素数个数(包括X和Y)。
- 样例输入
- 1 100
- 样例输出
- 25
- 思路:
- 这道题有个大坑!!!!给的数据有可能x>y!!!!
- c++代码实现
#include<iostream>
#include<cstdio>
using namespace std;
int main() {
int x,y,n,t;
cin>>x>>y;
if(x>y) {//进行交换,使得小数在前
t=x;x=y;y=t;
}
for(int i=x; i<=y; i++) {
int p=1; //是否为素数
if(i==1) //特判1不是素数
p=0;
for(int j=2; j*j<=i; j++) //筛法求素数
if(i%j==0) {
p=0;
break;
}
n+=p; //统计个数
}
cout<<n;
return 0;
}
例题5:codevs 1702 素数判定2
一个数,他是素数么?
设他为P满足(P<=263-1)
P
Yes|No
2
Yes
算法导论——数论那一节
注意Carmichael Number
c++代码实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int num[8] = {2,3,5,7,11,13,17,19};
int cnt=8;
ll mod_mul(ll a,ll b,ll mod) { // get the answer to a*b%n
ll res=0;
while(b) {
if (b&1) res=(res+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return res;
}
ll mod_exp(ll a,ll b,ll n) { // get the ans to a^b%n
ll res=1;
while(b) {
if (b&1) res=mod_mul(res,a,n);
a=mod_mul(a,a,n);
b>>=1;
}
return res;
}
bool check(ll n) {
if(n==2) return 1;
for(int i=0; i<8; i++) if(n==num[i]) return 1;
if(n==1 || (!(n&1))) return 0;
int k=0;
ll x,u=n-1,pre;
while(!(u&1)) {
k++;
u>>=1;
}
for(int i=0; i<cnt; i++) {
x=num[i];
x=mod_exp(x,u,n); // 费马小定理 a^(p-1)%p==1;
pre=x;
for(int j=0; j<k; j++) {
x=mod_mul(x,x,n); // add delete
if(x==1 && pre!=1 && pre!=n-1) return 0; // if last pre == 1 then x must == 1 else x==p-1;
pre=x; // second check the x of x^2%p=1 if 1 or p-1;
}
if(x!=1) return 0;
}
return 1;
}
int main() {
ll n;
cin>>n;
if(check(n)) cout<<"Yes";
else cout<<"No";
return 0;
}
例题6:孪生素数2
如m=100,n=6
则将输出100以内的所有相差6的孪生素数:如,
5 11
7 13
....
83 89
请按此规律输出数与数之间用半角空格区分,每一对一行.
第一行输入一个整数数m为一个范围(如100)
第二行输入一个整数k为目标孪生素数的公差(如6)
每行输出一对,最后一行输出:Total Is:?(?表示总共有几对这样的数,如果不存在则输出Total Is:0)
例如1:
50 2
例如2:
100 90
例如3:
200 199
例如1:
3 5
5 7
11 13
17 19
29 31
41 43
Total Is:6
例如2:
7 97
Total Is:1
例如3:
Total Is:0
m<=5000
c++代码实现
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int M = 5005;
int n,k,js;
int vis[M];
int pd(int x) {
if(!vis[x]) return 1;
else return 0;
}
void works(int end) {
vis[0]=vis[1]=1;
for(int i=2; i<=sqrt(end+0.5); i++)
if(vis[i]==0)
for(int j=i*2; j<=end; j+=i)
vis[j]=1;
}
int main() {
scanf("%d%d",&n,&k);
works(n);
for(int i=1; i<=n; i++)
if(i+k<=n && pd(i) && pd(i+k)) {
cout<<i<<" "<<i+k<<endl;
js++;
}
cout<<"Total Is:"<<js;
return 0;
}
例题7:1031 质数环
一个大小为N(N<=17)的质数环是由1到N共N个自然数组成的一个数环,数环上每两个相邻的数字之和为质数。如下图是一个大小为6的质数环。为了方便描述,规定数环上的第一个数字总是1。如下图可用1 4 3 2 5 6来描述。若两个质数环,数字排列顺序相同则视为本质相同。现在要求你求出所有本质不同的数环。
只有一个数N,表示需求的质数环的大小。如:
每一行描述一个数环,如果有多组解,按照字典序从小到大输出。如:
6
1 4 3 2 5 6
1 6 5 2 3 4
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
bool b[18],vis[999];
int n,a[18];
void works(int end) {
vis[0]=vis[1]=1;
for(int i=2; i<=sqrt(end+0.5); i++)
if(vis[i]==0)
for(int j=i*2; j<=end; j+=i)
vis[j]=1;
}
bool pd(int x,int y) { //判断和是否为素数;
int i=x+y;//代表和
if(!vis[i]) return 1;
else return 0;
}
int print() { //输出;
for(int j=1; j<=n; j++) cout<<a[j]<<" ";
cout<<endl;
}
int search(int t) {
for(int i=2; i<=n; i++) {
//判断该数是否可用 以及 该数与前一个数是否构成素数
//!b[i]是说b[i]没有被使用过
if((!b[i]) && pd(a[t-1],i)) {
a[t]=i;
b[i]=1; //将使用过的赋值为1;
if(t==n && pd(a[n],1)) print();
else search(t+1);
b[i]=0;//还原;
}
}
}
int main() {
cin>>n;
a[1]=b[1]=1;
if(n%2==1) {
cout<<endl;
return 0;
} else {
works(60); //第十七个素数为59
search(2);
}
return 0;
}
例题8: luogu P1217 [USACO1.5]回文质数 Prime Palindromes
题目见链接
c++代码实现
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 1e8 + 1;
const int M = 6e7;
int a,b;
int pd(int x) {
if(x==2 || x==3) return 1;
if(x%2==0 || x==1) return 0;
int j=3;
while(j<=sqrt(x) && x%j!=0) j+=2;
if(x%j==0) return 0;
else return 1;
}
void get_hui() {
int num,aa=a;
while(aa<10) {
if(pd(aa) && aa>=a) printf("%d\n",aa);
aa++;
}
for(int i=1; i<=9; i+=2) {
num=i*10+i;
if(num>b) return;
if(num>=a && pd(num)) printf("%d\n",num);
}
for(int i=1; i<=9; i+=2) {
for(int j=0; j<=9; j++) {
num=i*100+j*10+i;
if(num>b) return;
if(num>=a && pd(num)) printf("%d\n",num);
}
}
for(int i=1; i<=9; i+=2) {
for(int j=0; j<=9; j++) {
num=i*1000+j*100+j*10+i;
if(num>b) return;
if(num>=a && pd(num)) printf("%d\n",num);
}
}
for(int i=1; i<=9; i+=2) {
for(int j=0; j<=9; j++) {
for(int k=0; k<=9; k++) {
num=i*10000+j*1000+k*100+j*10+i;
if(num>b) return;
if(num>=a && pd(num)) printf("%d\n",num);
}
}
}
for(int i=1; i<=9; i+=2) {
for(int j=0; j<=9; j++) {
for(int k=0; k<=9; k++) {
num=i*100000+j*10000+k*1000+k*100+j*10+i;
if(num>b) return;
if(num>=a && pd(num)) printf("%d\n",num);
}
}
}
for(int i=1; i<=9; i+=2) {
for(int j=0; j<=9; j++) {
for(int k=0; k<=9; k++) {
for(int w=0; w<=9; w++) {
num=i*1000000+j*100000+k*10000+w*1000+k*100+j*10+i;
if(num>b) return;
if(num>=a && pd(num)) printf("%d\n",num);
}
}
}
}
for(int i=1; i<=9; i+=2) {
for(int j=0; j<=9; j++) {
for(int k=0; k<=9; k++) {
for(int w=0; w<=9; w++) {
num=i*10000000+j*1000000+k*100000+w*10000+w*1000+k*100+j*10+i;
if(num>b) return;
if(num>=a && pd(num)) printf("%d\n",num);
}
}
}
}
}
int main() {
scanf("%d%d",&a,&b);
get_hui();
return 0;
}
例题9: luogu P1218 [USACO1.5]特殊的质数肋骨 Superprime Rib
题目见链接
c++代码实现
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int pd(int x) {
if(x==2 || x==3) return 1;
if(x%2==0 || x==1) return 0;
int j=3;
while(j<=sqrt(x) && x%j!=0) j+=2;
if(x%j==0) return 0;
else return 1;
}
int len;
void work(int pre,int now) {
for(int i=1; i<=9; i++)
if(pd(pre*10+i)) {
if(now==len) printf("%d\n",pre*10+i);
else work(pre*10+i,now+1);
}
}
int main() {
scanf("%d",&len);
work(0,1);
return 0;
}