【叨叨叨】“衍生质数”相关的程序求解(欧拉筛质数)

终于放假了啊啊啊。

今天上课做的一道数学题,第二问用列举好麻烦,做的时候想写一个程序求,可能我太闲了吧()

关于“衍生质数”

已知k为合数,且1<k<100,当k的各数位上的数字之和为质数时,称此质数为k的“衍生质数”.

(1)若k的“衍生质数”为2,则k=_____;

(2)设集合 A={P(k)|P(k)为k的“衍生质数”},B={k|P(k)为k的“衍生质数”},则集合A∪B中元素的个数是_____

.——《步步高练透》数学人教B版必修1 培优课与集合有关的创新问题T10

程序求解

其实就是模拟嘛,求出A集合中的质数,再对应找B集合中元素。由于质数与合数不可能存在交集,即A与B不存在交集,因此最后的结果就是两个集合的元素个数之和了。

因为是自己实现,时间复杂度这种东西也不需要考虑,但是这里还是用质数筛法之一 ——欧拉筛来实现。

  • 欧拉筛

    欧拉筛又名线性筛,可以在O(n)的时间复杂度内筛出质数。

    在说这个之前,先了解一下埃氏筛(埃拉托斯特尼筛法)。其实很简单,如果一个数是质数,那么这个数的倍数必然是合数;反过来说,合数被质因数分解就印证了这一点。所以它的过程就是如果我们从小到大考虑每个数,然后同时把当前这个数的所有(比自己大的)倍数记为合数,那么运行结束的时候没有被标记的数就是质数了。(我记得九下数学课本上有这个的相关介绍,但是不考,所以不讲很难发现。)

    考虑还能再优化吗?例如12这个数,它的质因数(既是质数又是它的因数)有2,3这两个数,所以在这个过程中会被筛两遍,可我们只需要筛一遍就够了,这就会造成重复被筛的现象,这里只是拿12举例,如果是更大的数,重复被筛的次数就增多了,所以这种做法显然不太高效。因此欧拉筛就登场了。

    它的核心思想是:让每一个合数被其最小质因数筛到

    具体过程就是:从2~n,如果一个数x没被划掉,那么把它放进维护的质数表里,然后用x去划掉x * 质数表内元素(必然为合数);如果x没被划掉,不把它放进质数表,划掉 x * 质数表内元素(直到表内元素是它的倍数)。

    这么说有点抽象,例如:

    2没有被划掉,加入质数表,再用2去乘质数表内的每个数再划掉(2×2=4,划掉4)

    3没有被划掉,加入质数表,再用3去乘质数表内的每个数再划掉(2×3=6,划掉6;3×3=9,划掉9)

    下一个数是4,即使被划掉了也要遍历,只是意味着不是质数不需要加入质数表。

    用4去乘质数表内的数再划掉(2×4=8,划掉8),此时4是2的倍数,我们应该让每一个合数被最小质因数筛到,此时应该让2筛而不是4,所以我们不再用4去筛后面的数。

    ......

    后面的以此类推,最后没有被划掉的就是质数。

    以上为关于欧拉筛的简要介绍。

    模板可以参考:筛法 - OI Wiki (oi-wiki.org)

  • 相关说明

    这道数学题的范围在1~100,考虑到求解其他数据,设置了一个可输入的区间范围变量。

    代码丑陋,勿喷()。

  • Code

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=1e6;
    int l,r;
    vector<int> A,B;
    int s_A,s_B;
    bool not_prime[MAXN];
    void euler(int n,int s)
    {
    	for(int i=2;i<=n;i++)
    	{
    		if(!not_prime[i]&&i<=s) 
    		{
    			A.push_back(i);
    			s_A++;
    		}
    		for(int p:A)
    		{
    			if(p*i>n) break;
    			not_prime[p*i]=1;
    			if(i%p==0) break;
    		}
    	}
    	return;
    }
    void solve()
    {
    	for(int i=l;i<=r;i++)
    	{
    		if(i==1||!not_prime[i]) continue;
    		int x=i,s=0;
    		while(x) s+=x%10,x/=10;
    		if(find(A.begin(),A.end(),s)!=A.end())
    		{
    			B.push_back(i);
    			s_B++;
    		}
    	}
    	return;
    }
    void print()
    {
    	printf("\n");
    	printf("A={ ");
    	for(int p:A) printf("%d ",p);
    	printf("} %d个元素\n",s_A);
    	printf("\n");
    	printf("B={ ");
    	for(int p:B) printf("%d ",p);
    	printf("} %d个元素\n",s_B);
    	printf("\n");
    	printf("A∪ B共%d个元素\n",s_A+s_B);
    	return;
    }
    int main()
    {
    	printf("输入k的范围(l,r):");
    	scanf("%d %d",&l,&r);
    	l--,r--;
    	int x=r,s=0;
    	while(x) s+=x%10,x/=10;
    	euler(r,s);
    	solve();
    	print();
    	return 0; 
    } 

谢谢观看

  • 48
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FFTcwlath

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值