关于素数题的整合

解题思路:

下面是一个简单的判断素数的函数,但只能用较小的数,大些的数算法复杂度过高的性质就体现出来了

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;
}
  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
孪生素数是指两个相邻的素数,其差为2,如(3, 5)、(5, 7)、(11, 13)等。下面是一道关于C语言中孪生素数的OJ目: 目描述: 输入一个正整数n,出n以内的所有孪生素数(若存在)。输出格式为每个孪生素数对占一行,之间用一个空格隔开。如果不存在孪生素数,则输出"NO"。 输入格式: 输入一个正整数n(n<=1000000)。 输出格式: 按照格式输出符合条件的孪生素数对。 样输入: 20 样输出: 3 5 5 7 11 13 思路分析: 我们可以使用筛法来解决这道目。先用筛法出质,然后遍历质组,如果当前质与前一个质相差为2,则说明是一对孪生素数。 参考代码: ```c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAX_N 1000000 bool is_prime[MAX_N + 1]; // 标记是否为质 int prime[MAX_N + 1]; // 存储质 int prime_cnt = 0; // 记录质量 void sieve(int n) { for (int i = 2; i <= n; i++) { if (!is_prime[i]) { prime[prime_cnt++] = i; } for (int j = 0; j < prime_cnt && i * prime[j] <= n; j++) { is_prime[i * prime[j]] = true; if (i % prime[j] == 0) { break; } } } } int main() { int n; scanf("%d", &n); sieve(n); int prev_prime = -1; // 上一个质 for (int i = 0; i < prime_cnt; i++) { if (prev_prime != -1 && prime[i] - prev_prime == 2) { printf("%d %d\n", prev_prime, prime[i]); } prev_prime = prime[i]; } if (prev_prime == -1) { printf("NO\n"); } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值