质数与回文数与回文质数

本文章将讲解质数的判断方法、回文数的判断方法、以及某给定区间内回文质数的判断方法。不要问为什么写得如此粗制滥造,只因作者初次接触编程之广阔世界,能力有限,实 请 见 恕

质数

一个整数n是否为质数可以通过以下步骤来判断:

  1. 判断n是否小于2,若小于2则不是质数
  2. 从2开始,到n-1为止,判断n是否能被其中任意一个数整除,若能,则不是质数
  3. 若在2到n-1范围内都没有找到能整除n的数,则n为质数

C语言实现:

int is_prime(int n) {
    if (n < 2)
        return 0;
    for (int i = 2; i <= n - 1; i++){
        if (n % i == 0)
            return 0;
    }
    return 1;
}

回文数

回文数是指无论从左往右读还是从右往左读,所表示的数字都相同的数,例如101、1221、12321等。

判断回文数有两种方法,一种是拿第一位和最后一位作比较,如果相等,便比较第二位和倒数第二位…另一种是把这个数字倒过来,如果倒过来的数字和原来的数字相等,那么它便是一个回文数。

这里使用第二种方法,也就是先将一个整型数字 n 逆序输出,再拿这个新的数字 m 和原来的数字 n 作比较,如果二者相等,那么它便是一个回文数。

逆序输出

所以,第一步便是创造一个能够将整数逆序输出的函数。也就是将 n 的末尾放到 m 的首位,然后 n 除以 10 消除末位,直到 n 等于 0 为止:

int reverse_num(int n) {
    int m;
    for (m = 0; n > 0; n /= 10)
        m = m * 10 + n % 10;
    return m;
}

回文数判断

进一步地,便可以编写一个函数,判断 n 是不是回文数:

int judge_reverse_num(int n)
{
    int m, number;
    for (number = n, m = 0; number > 0; number /= 10)
        m = m * 10 + number % 10;

    if (n == m)
        return 1;
    else
        return 0;
}

回文质数

接下来,尝试找出给定范围 [a,b] 内的所有回文质数(5<=a<b<=100000000),也就是一亿以内某个区间既是回文数又是质数的数字。

这看起来貌似只是把前面两个函数复制到一起,然后利用 for 循环一个一个判断:

#include<stdio.h>

int is_prime(int n) {
    if (n < 2)
        return 0;
    for (int i = 2; i <= n - 1; i++) {
        if (n % i == 0)
            return 0;
    }
    return 1;
}

int judge_reverse_num(int n)
{
    int m, number;
    for (number = n, m = 0; number > 0; number /= 10)
        m = m * 10 + number % 10;

    if (n == m)
        return 1;
    else
        return 0;
}

int main() {
    int a, b;
    scanf("%d%d", &a, &b);
    if (a % 2 == 0)
        a++;
    for (a, b; a <= b; a++) {
        if (is_prime(a) == 0)
            continue;
        if (judge_reverse_num(a) == 0)
            continue;
        printf("%d\n", a);
    }
}

但 是 !
这样做虽然答案正确,却超 时 了 !

也就是说,这样判断过于低效,导致耗费的时间太多,我们需要优化自己的程序。

优化可以分为两部分进行:质数判断的优化和回文数判断的优化。

质数优化

判断一个数字是否是质数,真的要从 2 尝试到 n-1 吗?能不能减少范围?

假设 n 不是素数,则必然存在两个因子 a 和 b,使得 a*b=n。其中,a 和 b 至少有一个小于等于根号 n,另一个大于等于根号 n。如果不存在这样的 a 和 b,那么 n 就是一个素数。

假设有一个大于根号 n 的因子 c,那么另一个因子 d 必然小于根号 n,否则 c*d>n,与a和b作为两个因子的假设矛盾。因此,为了判断 n 是否是素数,我们只需要遍历到根号 n 就可以了,也就是说,如果在 2 到根号 n 之间找不到一个数能整除 n,那么 n 就是一个素数。这个方法比直接遍历到 n-1 要快得多,特别是当 n 很大的时候。

注意,求平方根的函数为 sqrt(n) ,需要头文件#include<math.h>:

int is_prime(int n) {
    if (n < 2)
        return 0;
    int max = sqrt(n) + 1;
    for (int i = 2; i <= max; i++) {
        if (n % i == 0)
            return 0;
    }
    return 1;
}

质数优化2

还可以进一步优化。比如,偶数一定不是质数,所以没有必要遍历所有的数字,只需要遍历所有的奇数就可以了,这可以省下一半的时间。

所以,我们将原来从a遍历到b的 for 循环:

for (a, b; a <= b; a++)

更改为如下:

if (a % 2 == 0)
    a++;
for (a, b; a <= b; a += 2)

如果 a 是偶数,则 a 加一,之后每次 for 循环只遍历奇数。

回文数优化

然而还是会超时,于是我们需要针对回文数的性质进行优化。

我们发现,数字位数为偶数的回文数都是11的倍数!也就是说,它们一定不是质数。例如 11 、 44 、 77 等两位数,2332 等四位数…

我们完全没有必要去判断这些数字,而是可以直接跳过。方法有很多,因为此处数据小于一亿,所以主函数可以如下优化:

int main() {
    int a, b;
    scanf("%d%d", &a, &b);

    if (a % 2 == 0)
        a++;

    for (a, b; a <= b; a += 2) {
        if (a > 1000 && a < 9999)
            a = 10001;
        else if (a > 100000 && a < 999999)
            a = 1000001;
        else if (a > 10000000 && a < 99999999)
            a = 100000001;
        if (is_prime(a) == 0)
            continue;
        if (judge_reverse_num(a) == 0)
            continue;
        printf("%d\n", a);
    }
}

回文数优化2

当然,这样进行判断显得过于…寒碜,而且仍旧不够快,远远不够。我们不妨转变思路,不是从所有的自然数中找出既是质数又是回文数的数字,而是直接从回文数中找出质数

也就是说,从小到大地,我们直接制造回文数

我们知道如下性质:

  1. 偶数位数的回文数不可能是质数,如123321。
  2. 最后一位是偶数的回文数不可能是质数。
  3. 1位数的大于等于5的回文质数显然只有5、7;2位数的回文质数有11。

综上所述,我们只需要生成3、5、7、9位的回文数即可:

#include<stdio.h>
#include<math.h>

//判断数字n是否是质数
int is_prime(int n) {
    if (n < 2)
        return 0;
    int max = sqrt(n) + 1;
    for (int i = 2; i <= max; i++) {
        if (n % i == 0)
            return 0;
    }
    return 1;
}
//主函数
int main() {
    int a, b;
    scanf("%d%d", &a, &b);
    int palindrome = 0;

    /*一百以内回文质数*/
    if (5 >= a && 5 <= b)
        printf("5\n");
    if (7 >= a && 7 <= b)
        printf("7\n");
    if (11 >= a && 11 <= b)
        printf("11\n");

    /*生成三位数回文数*/
    for (int d1 = 1; d1 <= 9; d1 += 2) {
        for (int mid = 0; mid <= 9; mid++) {
            palindrome = 100 * d1 + 10 * mid + 1 * d1;
            if (is_prime(palindrome) == 1 && palindrome >= a && palindrome <= b)
                printf("%d\n", palindrome);
        }
    }

    /*生成五位数回文数*/
    for (int d1 = 1; d1 <= 9; d1 += 2) {
        for (int d2 = 0; d2 <= 9; d2++) {
            for (int mid = 0; mid <= 9; mid++) {
                palindrome = 10000 * d1 + 1000 * d2 + 100 * mid + 10 * d2 + 1 * d1;
                if (is_prime(palindrome) == 1 && palindrome >= a && palindrome <= b)
                    printf("%d\n", palindrome);
            }
        }
    }

    /*生成七位数回文数*/
    for (int d1 = 1; d1 <= 9; d1 += 2) {
        for (int d2 = 0; d2 <= 9; d2++) {
            for (int d3 = 0; d3 <= 9; d3++) {
                for (int mid = 0; mid <= 9; mid++) {
                    palindrome = 1000000 * d1 + 100000 * d2 + 10000 * d3 + 1000 * mid + 100 * d3 + 10 * d2 + 1 * d1;
                    if (is_prime(palindrome) == 1 && palindrome >= a && palindrome <= b)
                        printf("%d\n", palindrome);
                }
            }
        }
    }

    /*生成九位数回文数*/
    for (int d1 = 1; d1 <= 9; d1 += 2) {//只有偶数才会是回文数,所以首位(末尾)必须是奇数
        for (int d2 = 0; d2 <= 9; d2++) {
            for (int d3 = 0; d3 <= 9; d3++) {
                for (int d4 = 0; d4 <= 9; d4++) {
                    for (int mid = 0; mid <= 9; mid++) {
                        palindrome = 100000000 * d1 + 10000000 * d2 + 1000000 * d3 + 100000 * d4
                            + 10000 * mid + 1000 * d4 + 100 * d3 + 10 * d2 + 1 * d1;
                        if (is_prime(palindrome) == 1 && palindrome >= a && palindrome <= b)
                            printf("%d\n", palindrome);
                    }
                }
            }
        }
    }
}

这样虽然代码麻烦了些,但是却能用最短的时间得到答案!可喜可贺,可喜可贺!

最后,总结一下算法中优化的部分:

  1. 判断n是否为质数,可以只遍历到n的平方根。
  2. 遍历区间寻找质数时,可以直接跳过所有的偶数。
  3. 数字位数为偶数的回文数一定是11的倍数,也就是说,一定不是质数。
  4. 与其遍历大区间(从 a 到 b 的所有自然数)判断两个条件(是否是回文数、是否是质数),不如遍历小区间(从 a 到 b 的所有回文数)判断一个条件(是否是质数)。
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值