如何计算可截素数

  • 什么是可截素数?

它本身是一个素数,如果从左往右逐一截去数字,剩下的仍然都是素数,如果从右往左逐一截去数字,剩下的也仍然都是素数。

例如:3797就是一个可截素数。

从左往右截去数字:797,97,7 都是素数;

从右往左截去数字:379,37,3 都是素数。

  • 题目:求前11个可截素数的和。(注:2,3,5,7 不是可截素数

  • 题目解析

这个题目依然是对素数的处理,对于这种对连续且大量的数据进行素数的判断的问题,我们可以使用素筛法或线性筛法来获得素数表。由于素筛法的时间复杂度是O(log(log(n))) ,而线筛法的时间复杂度可以达到O(n), 当数据规模不大时,二者的程序运行效率的差异是不明显的,但当数据规模非常庞大的时候,线筛法更优。

所以,第一步,构建素数表。

第二步,遍历素数表,判断当前素数值是否满足可截素数的特点。

可截素数的特点就是从左到右和从右到左截去数据,,所得数值依然是素数。

  1. 从左到右截去数字

        以3797为例: 3797 \rightarrow  797 \rightarrow 97 \rightarrow 7 ;可见,从左往右截去数字,就是一个不断去掉最高位的过程。如何去掉一个数N的最高位?用 N 对其最高位的权重 取模。

         3797 的最高位是3,3的权重是1000, 所以 3797 \rightarrow 797 这个截去3的操作就是 3797 % 1000 ;

        797的最高位是7,7的权重是100,所以797 \rightarrow 97 就是 797 %100;

        97 的最高位是9,9的权重是10,所以97 \rightarrow 7 就是97 % 10;

        7已经是个位数,截去操作结束。

        2. 从右往左截去数字

        依然以3797为例:3797 \rightarrow 379 \rightarrow 37 \rightarrow 3; 从右往左截去数字就是一个不断去掉最低位的过程。截掉最低位,就是数字规模缩小10倍,即 N /10 

  • 代码实现
#include <stdio.h>
#include <math.h>

#define MAX_N 2000000

int prime[MAX_N+5] = {0}; //从prime[1]开始存放素数, prime[0]表示prime数组中素数总个数
int is_prime[MAX_N+5] = {1,1,0}; //用于标记是否为素数,如果i为素数,则is_prime[i] == 0,否则,is_prime[i] == 1;

//利用线性筛法构建素数表
void init_prime(){
    for(int i = 2;i<= MAX_N;i++){
        if(!is_prime[i]) prime[++prime[0]] = i; //is_prime[i] == 0, 表示数字i为素数,存入prime
        for(int j = 1; j <= prime[0];j++){
            if(prime[j] * i > MAX_N) break;
            is_prime[prime[j] * i] = 1;
            if(i % prime[j] == 0) break;
        }
    }
    return;
}

//将当前素数n执行从左到右截去和从右到左截去后,如果依然是素数,返回1,否则返回0
int is_val(int n){
    int digit = floor(log10(n)); //求n的位数
    int temp = n;
    
    //从左向右截去数字
    for(int i = digit; i >0;i--){
        if(is_prime[temp % (int)pow(10,i)]) return 0;
    }
    
    //从右向左截去数字
    for(int i = 1 i <= digit; i++){
        if(is_prime[temp / (int)pow(10,i)]); return 0;
    }
    
    return 1;
}

int main(){
    init_prime();
    int flag = 11;
    long long sum = 0;
    
    for(int i = 1; i <= prime[0];i++){
        if(prime[i] > 10 && is_val(prime[i])){
            sum += prime[i];
            if(--flag == 0) break;
        }
    }
    printf("sum = %d\n",sum);
    return 0;
}

代码优化:

int is_val(int n){
    //从左往右截去
    int h = pow(10,floor(log10(n))),temp = n;
    while(n){
        if(is_prime[n]) return 0;
        n % h;
        h /= 10;
    }
    
    //从右往左截去
    while(temp){
        if(is_prime[temp]) return 0;
        temp /= 10;
    }
    
    return 1;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xinran0703

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

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

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

打赏作者

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

抵扣说明:

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

余额充值