解题思路:
下面是一个简单的判断素数的函数,但只能用较小的数,大些的数算法复杂度过高的性质就体现出来了
int isprime(int a)
{
if(a<2)
{
return 0;
}
else if(a==2)
{
return 1;
}
for(int i=2;i<a;i++)
{
if(a%i==0)
return 0;
}
return 1;
}
优化1:
判断素数也就是看因子对,对于素数只有自身和1这一对因子对,所以我们也可以看作判断因子对,那么比如判断一个数a是否是素数,只用遍历2到sqrt(a)的数 i 是否是a的因子即可,那么循环条件是i*i<=a
int isprime(int a)
{
if(a<2)
{
return 0;
}
else if(a==2)
{
return 1;
}
for(int i=2;i*i<=a;i++)
{
if(a%i==0)
return 0;
}
return 1;
}
下面前五道题,都用的优化1,第六道题时,优化1的算法复杂度仍然还是高了,会超时,继续优化,当然这三种判断素数的函数都还只是判断单个元素的
优化2:
int isprime(int a) {//判断某个元素是否是素数,在这里就是方法中已经筛选的素数
if (a < 2) {
return 0;
}
if (a == 2 || a == 3) {
return 1;
}
if (a % 2 == 0 || a % 3 == 0) {
return 0;
}
for (int i = 5; i * i <= a; i += 6) {//这里判断的理由是数学上有一个定理,只有形如6n-1和6n+1的自然数可能是素数,这里的n是大于等于1的整数
if (a % i == 0 || a % (i + 2) == 0) {
return 0;
}
}
return 1;
}
优化3:
埃氏筛法,完整的看第六题,可以用目录快速定位
void is_prime(size_t l,size_t r)
{
for(size_t i=2;i<=sqrt(r);i++)
{
if(isprime(i))
{
for(size_t j=l/i*i;j<=r;j+=i)
{
if(j>=l&&j!=i)
{
res[j-l]=l;
}
}
}
}
}
1.筛选N以内的素数(dotcpp题号为1022)
题目描述
用简单素数筛选法求N以内的素数。
输入格式
N
输出格式
2~N的素数
样例输入
100
样例输出
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97
解题思路:
素数是只能整除1和自己的数,且1不是素数
参考代码:
#include<stdio.h>
int isprime(int a)
{
if(a<2)
{
return 0;
}
else if(a==2)
{
return 1;
}
for(int i=2;i*i<=a;i++)
{
if(a%i==0)
return 0;
}
return 1;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
if(isprime(i))
{
printf("%d\n",i);
}
}
return 0;
}
2.计算素数和(dotcpp题号为1054)
题目描述
输入两个正整数m和n(m<n),求m到n之间(包括m和n)所有素数的和,要求定义并调用函数isprime(x)来判断x是否为素数(素数是除1以外只能被自身整除的自然数)。
输入格式
m n
输出格式
素数和
样例输入
2 3
样例输出
5
解题思路:
这道题也就是定了循环的起始位置和结束位置,还有一个累加器
参考代码:
#include<stdio.h>
int isprime(int a)
{
if(a<2)
{
return 0;
}
else if(a==2)
{
return 1;
}
for(int i=2;i<a;i++)
{
if(a%i==0)
return 0;
}
return 1;
}
int main()
{
int n,m;
scanf("%d%d",&m,&n);
int sum=0;
for(int i=m;i<=n;i++)
{
if(isprime(i))
{
sum+=i;
}
}
printf("%d",sum);
return 0;
}
3.素数求和(dotcpp题号为2206)
题目描述
现在给你N个数(0<N<1000),要求你写出一个程序,找出这N个数中的所有素数,并求和。
输入格式
第一行给你N,代表有多少个数据,
接下来的N个数为要测试的数据,(N<=1000000)。
输出格式
每组测试数据结果占一行,输出给出的测试数据的所有素数和。
样例输入
3
100
10
999999
样例输出
1060
17
37550402023
解题思路:
就是上一道题的对多组输入的处理
参考代码:
#include<stdio.h>
#include<math.h>
int isprime(int a)
{
if(a<2)
{
return 0;
}
else if(a==2)
{
return 1;
}
for(int i=2;i*i<=a;i++)
{
if(a%i==0)
return 0;
}
return 1;
}
int main()
{
int n;
scanf("%d",&n);
long long a=0,sum=0;
for(int i=0;i<n;i++)
{
sum=0;
scanf("%d",&a);
if(a<2)
continue;
for(int i=2;i<=a;i++)
{
if(isprime(i))
{
sum+=i;
}
}
printf("%lld\n",sum);
}
return 0;
}
4.素数回文(dotcpp题号为1250)
题目描述
小王对既是素数又是回文的数特别感兴趣。比如说151既是素数又是个回文。现在小王想要你帮助他找出某个范围内的素数回文数,请你写个程序找出 a 跟b 之间满足条件的数。(5 <= a < b <= 100,000,000);
输入格式
输入a和b(5 <= a < b <= 100,000,000)
输出格式
按从小到大输出a,b之间所有满足条件的素数回文数
样例输入
5 500
样例输出
5
7
11
101
131
151
181
191
313
353
373
383
解题思路:
加了个对回文数的判断,判断回文数时,要把数字转换为字符型,不然不好判断,转换后,再用双指针法即可
参考代码:
#include<stdio.h>
# define MAXSIZE 100000000
char b[MAXSIZE]={'\0'};
int isprime(int a)
{
if(a<2)
{
return 0;
}
else if(a==2)
{
return 1;
}
for(int i=2;i*i<a;i++)
{
if(a%i==0)
return 0;
}
return 1;
}
int palindrome(int a)//判断回文数
{
if(a<10)
return 1;
int count=0;
while(a)
{
b[count]=a%10+'0';//数字转换为字符型,存入数组
count++;
a=a/10;
}
char *p=b,*q=b+count-1;//一个指针指向头部,另一个指向尾部
while(q>p)
{
if(*p!=*q)
return 0;
p++;
q--;
}
return 1;
}
int main()
{
int n,m;
scanf("%d%d",&m,&n);
for(int i=m;i<=n;i++)
{
if(isprime(i))
{
if(palindrome(i))
printf("%d\n",i);
}
}
return 0;
}
5.送分题素数(dotcpp题号为1259)
题目描述
输出100->200之间的素数的个数,以及所有的素数。
输入格式
无
输出格式
100->200之间的素数的个数,以及所有的素数。
样例输入
无
样例输出
21
101 103 … 197 199
解题思路:
没什么新颖的地方,注意下输出的要求,先用一个数组存素数,
参考代码:
#include<stdio.h>
#include<math.h>
# define MAXSIZE 100
int isprime(int a)
{
if(a<2)
{
return 0;
}
else if(a==2)
{
return 1;
}
for(int i=2;i*i<=a;i++)
{
if(a%i==0)
return 0;
}
return 1;
}
int main()
{
int sum=0;
int a[100]={0};
int count=0;
for(int i=100;i<=200;i++)
{
if(isprime(i))
{
sum++;
a[count]=i;
count++;
}
}
printf("%d\n",sum);//先输出素数总个数,在下一行输出每个素数
for(int i=0;i<count;i++)
printf("%d ",a[i]);
return 0;
}
6.蓝桥杯算法提高VIP-找素数(dotcpp题号为1525)
题目描述
给定区间[L, R] , 请计算区间中素数的个数。
输入格式
两个数L和R。
数据规模和约定
2 < = L < = R < = 2147483647 R-L < = 1000000
输出格式
一行,区间中素数的个数。
样例输入
2 11
样例输出
5
解题思路:
这题给的数据范围着实逆天,提交的时候不是超时就是溢出,用数组来模拟这个区间,但由于数据范围太大,所以我们的数组序号以当前元素减去L的值作为序号,
埃式筛法的思路非常简单,就是用已经筛选出来的素数去过滤所有能够被它整除的数。
由于欧拉筛还没怎么看明白,不会写(。_。),所以应该还有更优解
参考代码:
#include<stdio.h>
#include<math.h>
#define MAXSIZE 1000002
int res[MAXSIZE]={0};//初始全部为0,为0是素数,为1是非素数
size_t l,r;
int sum=0;
int isprime(int a) {//判断某个元素是否是素数,在这里就是方法中已经筛选的素数
if (a < 2) {
return 0;
}
if (a == 2 || a == 3) {
return 1;
}
if (a % 2 == 0 || a % 3 == 0) {
return 0;
}
for (int i = 5; i * i <= a; i += 6) {//这里判断的理由是数学上有一个定理,只有形如6n-1和6n+1的自然数可能是素数,这里的n是大于等于1的整数
if (a % i == 0 || a % (i + 2) == 0) {
return 0;
}
}
return 1;
}
void is_prime(size_t l,size_t r)
{
for(size_t i=2;i<=sqrt(r);i++)
{
if(isprime(i))
{
for(size_t j=l/i*i;j<=r;j+=i)
{
if(j>=l&&j!=i)
{
res[j-l]=l;
}
}
}
}
}
int main()
{
scanf("%zu%zu",&l,&r);
is_prime(l,r);
for(size_t j=0;j<=r-l;j++)
{
if(res[j]==0)
sum++;
}
printf("%d",sum);
return 0;
}
7.蓝桥杯算法提高VIP-素数求和(dotcpp题号为1554)
题目描述
输入一个自然数n,求小于等于n的素数之和
输入格式
数据规模和约定
测试样例保证 2 < = n < = 2,000,000
输出格式
无
样例输入
2
样例输出
2
解题思路:
找一道简单一点,又对算法复杂度有要求的,就这道题,展示下欧拉筛
欧拉筛就是埃氏筛基础上再初始化一个数组存已经判断出来的素数,避免了一个元素被重复筛掉
参考代码:
#include<stdio.h>
#include<math.h>
# define MAXSIZE 2000001
size_t n;
int res[MAXSIZE]={0};
int b[MAXSIZE];//存已经判断出的素数
int k=0;
void isprime()
{
for(size_t i=2;i<=n;i++)
{
if(res[i]==0)//若i是素数时
{
b[k++]=i;//将找到的质数放入数组
}
for(size_t j=0;j<=k&&b[j]*i<=n;j++)//在这个循环中,我们遍历已经判断出的素数数组 b,并将它们与当前数字 i 相乘得到的数标记为非素数。
//循环条件为 j <= k && b[j] * i <= n,即 j 小于等于已经判断出的素数个数 k,并且 b[j] * i 小于等于 n。
{
res[b[j]*i]=1;
if(i%b[j]==0)
break;
}
}
}
int main()
{
scanf("%zu",&n);
isprime();
size_t sum=0;
for(size_t i=2;i<=n;i++)//遍历出是素数的数
{
if(res[i]==0)
sum+=i;
}
printf("%zu",sum);
return 0;
}
8.素数对(dotcpp题号为2945)
题目描述
两个相差为2的素数称为素数对,如5和7,17和19等,本题目要求找出所有两个数均不大于n的素数对。
输入格式
一个正整数n。1 <= n <= 10000。
输出格式
所有小于等于n的素数对。每对素数对输出一行,中间用单个空格隔开。若没有找到任何素数对,输出empty。
样例输入
100
样例输出
3 5
5 7
11 13
17 19
29 31
41 43
59 61
71 73
解题思路:
这题用欧拉筛法,不怎么会就要多用,注意小于当输入的n小于3时,没有素数对,输出empty
还有因为输出是素数对的原因,最后遍历素数输出时循环的限制应该是i+2<=n,
参考代码:
#include<stdio.h>
#include<math.h>
# define MAXSIZE 2000001
size_t n;
int res[MAXSIZE]={0};
int b[MAXSIZE];
int k=0;
void isprime()
{
for(size_t i=2;i<=n;i++)
{
if(res[i]==0)
{
b[k++]=i;
}
for(size_t j=0;j<=k&&b[j]*i<=n;j++)
{
res[b[j]*i]=1;
if(i%b[j]==0)
break;
}
}
}
int main()
{
scanf("%zu",&n);
if(n<3)
{
printf("empty");
return 0;
}
isprime();
size_t sum=0;
for(size_t i=2;i+2<=n;i++)
{
if(res[i]==0&&res[i+2]==0)
{
printf("%d %d\n",i,i+2);
}
}
return 0;
}
9.回文素数(dotcpp题号为2956)
题目描述
一个数如果从左往右读和从右往左读数字是相同的,则称这个数是回文数,如121,1221,15651都是回文数。给定位数n,找出所有既是回文数又是素数的n位十进制数。(注:不考虑超过整型数范围的情况)。
输入格式
位数n,其中1<=n<=9。
输出格式
第一行输出满足条件的素数个数。
第二行按照从小到大的顺序输出所有满足条件的素数,两个数之间用一个空格区分。
样例输入
1
样例输出
4
2 3 5 7
解题思路:
这题正常解法,对空间巨大,到7差不多就会溢出,
重点:在素数领域有一个性质,偶数位数的数除了11以外全部不是回文素数
想知道原理的可以去查下,我也不知( ´・・)ノ(._.`)
这样的话,除了n==2时,n%2=0时都是输出0
这样就只用处理奇数了,为了对回文数判断的方便建议n==1的情况,也作单独处理
因为只用对奇数进行处理,可以每次得到一个数的一半,然后构造另一半,以构造回文数的方式,再对构造好的回文数判断是否为素数
存素数的数组空间设6000以上,不然不够,n为9时,素数数量为5172
参考代码:
#include<stdio.h>
#include<math.h>
int isprime(size_t a)//判断素数
{
if(a<2)
{
return 0;
}
else if(a==2||a==3)
{
return 1;
}
else if(a%2==0||a%3==0)
{
return 0;
}
for(size_t i=5;i<=sqrt(a);i+=6)
{
if(a%i==0||a%(i+2)==0)
return 0;
}
return 1;
}
size_t palind(size_t a)//构造回文数
{
size_t c=a/10;
while(c)
{
a=a*10+c%10;
c=c/10;
}
return a;
}
int main()
{
int n;
scanf("%d",&n);
if(n==1)
{
printf("4\n2 3 5 7");
}
else if(n==2)
{
printf("1\n11");
}
else if(n%2==0)//为偶数位数的数时
{
printf("0");
}
else
{
int a=pow(10,(n-1)/2);//取一半,范围的最小值
int b=pow(10,n/2+1);//范围的最大值
size_t c;
long long m[50000]={0};//因为数过大,要用long long
int count=0;//素数数量
for(size_t i=a;i<b;i++)
{
c=palind(i);//返回构造的回文数
if(isprime(c))//判断该回文数是否是素数
{
m[count]=c;
count++;
}
}
printf("%d\n",count);
for(int i=0;i<count;i++)
{
printf("%d ",m[i]);
}
}
return 0;
}
10.区间内的真素数(dotcpp题号为2968)
题目描述
找出正整数 M 和 N 之间(N 不小于 M)的所有真素数。
真素数的定义:如果一个正整数 P 为素数,且其反序也为素数,那么 P 就为真素数。
例如,11,13 均为真素数,因为11的反序还是为11,13 的反序为 31 也为素数。
输入格式
输入两个数 M 和 N,空格间隔,1 <= M <= N <= 100000。
输出格式
按从小到大输出 M 和 N 之间(包括 M 和 N )的真素数,逗号间隔。如果之间没有真素数,则输出 No。
样例输入
10 35
样例输出
11,13,17,31
解题思路:
在判断区间内素数的基础上,加了一个数反序是否也是素数的限制,用一个while循环以取余的方式得出数的反序,再判断该数是否是素数即可
参考代码:
#include<stdio.h>
#include<math.h>
#define MAXSIZE 100001
int a[MAXSIZE]={0};
int isprime(int m)//判断单个数是否是素数
{
if(m<2)
{
return 0;
}
if(m==2||m==3)
{
return 1;
}
if(m%2==0||m%3==0)
{
return 0;
}
for(int i=5;i<=sqrt(m);i+=6)这里判断的理由是数学上有一个定理,只有形如6n-1和6n+1的自然数可能是素数,这里的n是大于等于1的整数
{
if(m%i==0||m%(i+2)==0)
{
return 0;
}
}
return 1;
}
void is_prime(int m,int n)//埃氏筛法
{
for(int i=2;i<=sqrt(n);i++)
{
if(isprime(i))//用素数来筛区间的非素数
{
for(int j=m/i*i;j<=n;j+=i)
{
if(j!=i&&j>=m)
{
a[j-m]=1;
}
}
}
}
}
int faisprime(int m)//对数做反序处理并判断是否是素数
{
int c=0;
while(m)
{
c=c*10+m%10;
m/=10;
}
if(isprime(c))//判断是否是素数,若是返回1
{
return 1;
}
return 0;
}
int main()
{
int m,n;
scanf("%d%d",&m,&n);
is_prime(m,n);
int t=0;
for(int i=0;i<=n-m;i++)
{
if(a[i]==0&&faisprime(i+m))//存进数组时是j-m,这里判断时要加回来
{
if(t!=0)
printf(",");
printf("%d",i+m);
t++;
}
}
return 0;
}
11.最小素数拆分(头歌平台上的)
题目描述
现在给定一个正整数N,求N最少表示成多少个素数的和。
提示:素数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
输入格式
一个正整数
输出格式
一个正整数
输入输出示例
输入
3
输出
1
解题思路:
这题不得不说,初见确实很懵,在网上搜了下,就是要用
哥德巴赫猜想:任意大于2的偶数都可以拆分成两个质数之和。
那这样对于一个数n的拆分就有三种情况,
1.当n自身为素数时,那么就输出1
2.当n为偶数时,根据猜想,输出2
3.当n为奇数时,题目目的是将n表示为素数之和,那么就先拆出一个质数,也就是n减去一个小于n的质数k,这里有两种情况
(1)如果n最少由两个质数构成,那n-k和k必定不可能都是奇数,因为质数必定是奇数,而两奇数相加,必定是偶数,显然不对,那是偶数又为奇数的只有2,那么k为2,那么判定n是奇数时最少由两个质数构成的条件是n-2是否为质数
(2)当n-2不为质数时,再利用一下猜想,n为奇数,那么n-3就为偶数,3为素数,n-3根据猜想可以拆为2个素数,那么此时最少为3个素数构成
参考代码:
#include<stdio.h>
#include<math.h>
#define MAXSIZE 100000
int a[MAXSIZE]={1,1};
int b[MAXSIZE];
int k=0;
void is_prime(int n)//这里用了欧拉筛法
{
for(int i=2;i<=n;i++)
{
if(a[i]==0)
b[k++]=i;
for(int j=0;j<=k&&b[j]*i<=n;j++)
{
a[b[j]*i]=1;
if(i%b[j]==0)
break;
}
}
}
int main()
{
int n;
scanf("%d",&n);
int count=0;
is_prime(n);
if(a[n]==0)//自身为素数时
{
printf("1");
}
else
{
if(n%2==0)//当n为偶数时
{
printf("2");
}
else{
if(a[n-2]==0)//解题思路中第三种情况的(1)
{
printf("2");
}
else{//第三种情况的(2)
printf("3");
}
}
}
return 0;
}
12.哥德巴赫曾猜测
题目描述
德国数学家哥德巴赫曾猜测:任何大于6的偶数都可以分解成两个素数(素数对)的和。但有些偶数可以分解成多种素数对的和,如: 10=3+7,10=5+5,即10可以分解成两种不同的素数对
输入格式
输入任意的>6的正偶数(<32767)
输出格式
试求给出的偶数可以分解成多少种不同的素数对(注: A+B与B+A认为是相同素数对)
样例输入
1234
样例输出
25
解题思路:
主要思路就是以一个for循环,遍历小于输入的n的数i,判断i和n-i是否为素数,再定义一个计数器count,若是素数则count++,
要优化的话主要是从遍历小于输入的n的数i的范围入手,我嘛,只想到了两点:
- A+B和B+A题目说了是相同素数对,那么对于大于n/2部分的数,也就不用遍历了
- 还有就是输入的都是>6的偶数,2是素数中唯一的偶数,如果i起始值从2开始,偶数减偶数必然也是偶数,在>6的数中,也就不存在有素数对中有2了,所以i的起始值应该从3开始
- 至于对于判断素数函数的优化,在最开头都写了,就不说了
参考代码:
#include<stdio.h>
#include<math.h>
int isprime(int a)
{
for(int i=2;i<=sqrt(a);i++)
{
if(a%i==0)
return 0;
}
return 1;
}
int main()
{
int n;
scanf("%d",&n);
int count=0;
for(int i=3;i<=n/2;i++)
{
if(isprime(i)&&isprime(n-i))//判断i和n-i是否为素数
count++;//计数器
}
printf("%d",count);
return 0;
}
13.素数距离问题
任务描述
现在给出你一些数,要求你写出一个程序,输出这些整数相邻最近的素数,并输出其相距长度。如果左右有等距离长度素数,则输出左侧的值及相应距离。
如果输入的整数本身就是素数,则输出该素数本身,距离输出0
输入格式:
第一行给出测试数据组数N(0<N<=10000)
接下来的N行每行有一个整数M(0<M<1000000),
输出格式:
每行输出两个整数 A B.
其中A表示离相应测试数据最近的素数,B表示其间的距离。
输入样例:
3
6
8
10
输出样例:
5 1
7 1
11 1
解题思路
对输入的数往两边遍历即可
参考代码
//请在此输入你的代码,复杂程序可先在Dev C++中运行调试后再提交
#include<stdio.h>
#include<math.h>
int isprime(int x)//判断素数
{
if(x<2)
return 0;
for(int i=2;i*i<=x;i++)
{
if(x%i==0)
return 0;
}
return 1;
}
void _isprime(int m)
{
int distance=0;//左侧距离
int distance_r=0;//右侧距离
if(isprime(m))//若输入的整数本身就是素数
{
printf("%d %d",m,distance);
return;
}
int left=m-1,right=m+1;
distance=1,distance_r=1;
while(1)
{
if(isprime(left))
{
break;
}
else
{
if(left%2==0)
{
distance++;
left--;
}
else
{
distance+=2;
left-=2;
}
}
if(isprime(right))
{
break;
}
else
{
if(right%2==0)
{
distance_r++;
right++;
}
else
{
distance_r+=2;
right+=2;
}
}
}
if(distance<=distance_r)
{
printf("%d %d",left,distance);
}
else
{
printf("%d %d",right,distance_r);
}
}
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
int m;
scanf("%d",&m);
_isprime(m);
printf("\n");
}
return 0;
}