求回文数算法

问题:

    求第N个回文数palindrome。

    一个正数如果顺着和反过来都是一样的(如13431,反过来也是13431),就称为回文数。约束:

    回文数不能以0开头

    回文数从1开始。

首先我们要写一个算法求回文数。刚开始我想到用用字符串来存储数,然后判断原序和逆序是否相等。

void func1(char a[])
{
    printf("%d",strlen(a));
 
    char *p=a;
        char *q=a+strlen(a)-1;  
        bool flag=true;
        while(q>p)
        {
            if(*q!=*p)
            {
                flag=false;
                break;
            }
            q--;p++;
        }
        if(flag)
            printf("%s 是回文数
",a);
        else
            printf("%s 不是回文数
",a);
         

}
int main()
{
     
    char s[50];
    while(scanf("%s",s)!=0)
    {
      printf("%d",strlen(s));
       func1(s);
         
    }

注意,用strlen的时候只检测什么时候到‘'位置,与sizeof无关,如果是sizeof的话char a[]做函数参数a会降级为指针

虽然这样做可以,但还是有点小问题,如果输入010,输出回文。不满足回文的定义。

回文数不能以0开头

  一开始就思考使用循环:从1开始,判断该数是否是回文数,然后用一

个计数器记下回文数,一直到计数器得到N,返回第N个回文数。比较常用的是以下这种方法来判断是

否回文数:

static boolean isPN(int num) {
    int o = num;
    int tmp = 0;
    //使用循环把数字顺序反转
    while(num != 0) {
        tmp *= 10;
        tmp += num % 10;
        num /= 10;
    }
    //如果原始数与反转后的数相等则返回true
    if(tmp == o) 
        return true;
    return false;
}

这种思路的确可得到正确结果,但随着用来测试的N的增大,效率的问题就浮现了。因为是一重

循环,效率是O(n)。所以当N非常大时,所花的时间还是十分大的。

另一种思路:

    回文数的个数其实是有规律的。如:

    1位回文数: 9个

    2位回文数: 9个

    3位回文数: 90个

    4位回文数: 90个

    5位回文数: 900个

    6位回文数: 900个

    …

    我们看到9、90、900,是不是很有规律,那是什么原因?很简单,我们把回文数拆开两半

[123321]来看。两半的变化一样的,那我们只算其中一半就行了。首位不能是0,所以左半最小为

100,最大为999,共有999-100+1=900个,如此类推。

    所以我们可以基于以下原则:

    1、 回文数的数位每增长2,回文数的个数为原来的10倍。如从个位回文数到百位回文数,个数

从9个变为90个。

    2、 个位回文数的个数是9,1、2、3、…、9。

    总之理解原理后一切好办,步骤就偷懒不写了,看代码吧!

核心代码:

static long find(int index) {
        int count = 0;            
        int number = 9;                        //记录数位上的回文数,如个位回文数为9
        int w = 0;                            //记录数位
        
        long half;                            //保存回文数的左半边的结果
        long h = 1;                            //回文数的左半边的起始基数
        long res;                            //结果
        
        while(true) {
            if(w > 0 && w%2 == 0) {            //每进两个数位,回文数乘以10
                number *= 10;
            }
            w++;                            //数位加一
            if(count + number > index)        //回文数大于查找的回数,跳出
                break;
                
            count += number;                //回文数加上当前数位上的回文数
        }
        
        index -= count;                        //在当前数位上的位置。如w=5,index=50,则万位上的第50个回文数是我们所求
        
        for(int i = 0; i < (w-1) / 2; i++) {    //求回文数的左半边的基数,如回文数在万位上,则为100
            h *= 10;
        }
        
        half = h + index;                        //回文数的左半边,如100 + 50 = 150
        
        res = half;
        
        if(w%2 != 0)                            //如果为奇数,则中间那个数不必算入右半边了!
            half /=10;
            
        while(half != 0) {                        //拼接回文数
            res = res *10 + half % 10;
            half /= 10;
        }
        
        return res;
    }

全部代码:

package com.icescut.classic.algorithm;
//数位指个位,十位,百位,千位。。。
class PalindromicNumber {
    public static void main(String[] args) {
        int input = Integer.parseInt(args[0]);
        long res = find(input);
        System.out.println("res:" + res);
        
    }
    
    static long find(int index) {
        int count = 0;            
        int number = 9;                        //记录数位上的回文数,如个位回文数为9
        int w = 0;                            //记录数位
        
        long half;                            //保存回文数的左半边的结果
        long h = 1;                            //回文数的左半边的起始基数
        long res;                            //结果
        
        while(true) {
            if(w > 0 && w%2 == 0) {            //每进两个数位,回文数乘以10
                number *= 10;
            }
            w++;                            //数位加一
            if(count + number > index)        //回文数大于查找的回数,跳出
                break;
                
            count += number;                //回文数加上当前数位上的回文数
        }
        
        index -= count;                        //在当前数位上的位置。如w=5,index=50,则万位上的第50个回文数是我们所求
        
        for(int i = 0; i < (w-1) / 2; i++) {    //求回文数的左半边的基数,如回文数在万位上,则为100
            h *= 10;
        }
        
        half = h + index;                        //回文数的左半边,如100 + 50 = 150
        
        res = half;
        
        if(w%2 != 0)                            //如果为奇数,则中间那个数不必算入右半边了!
            half /=10;
            
        while(half != 0) {                        //拼接回文数
            res = res *10 + half % 10;
            half /= 10;
        }
        
        return res;
    }
}

说明:

    因为按规律计算,效率为O1,不论数量级多大,能马上得到结果(不发生溢出情况下)。对比循环判断回文数的方法,大大的改善了。

转自:http://www.cnblogs.com/icescut/archive/2009/11/09/PalindromicNumber.html

缺8数、回文数的秘密

 人们把12345679叫做“缺8数”,这“缺8数”有许多让人惊讶的特点,比如用9的倍数与它相乘,乘积竟会是由同一个数组成,人们把这叫做“清一色”。比如:

 

12345679*9=111111111

12345679*18=222222222

12345679*27=333333333

……

12345679*81=999999999

这些都是9的1倍至9的9倍的。

还有99、108、117至171。最后,得出的答案是:

12345679*99=1222222221

12345679*108=1333333332

12345679*117=1444444443

 。。。

12345679*171=2111111109

 

回文数

中文里,有回文诗句、对联,如:"灵山大佛,佛大山灵","客上天然居,居然天上客"等等,都是美妙的符合正念倒念都一样的回文句.

回文数则是有类似22、383、5445、12321,不论是从左向右顺读,还是从右向左倒读,结果都是一样的特征.许多数学家着迷于此。

回文数中存在无穷多个素数11,101,131,151,191……。除了11以外,所有回文素数的位数都是奇数。道理很简单:如果一个回文素数的位数是偶数,则它的奇数位上的数字和与偶数位上的数字和必然相等;根据数的整除性理论,容易判断这样的数肯定能被11整除,所以它就不可能是素数。附整除原理11整除的特征。

能被11整除的数的特征
把一个数由右边向左边数,将奇位上的数字与偶位上的数字分别加起来,再求它们的差,如果这个差是11的倍数(包括0),那么,原来这个数就一定能被11整除.
例如:判断491678能不能被11整除.
—→奇位数字的和9+6+8=23

—→偶位数位的和4+1+7=12 23-12=11
因此,491678能被11整除.
这种方法叫"奇偶位差法".
除上述方法外,还可以用割减法进行判断.即:从一个数里减去11的10倍,20倍,30倍……到余下一个100以内的数为止.如果余数能被11整除,那么,原来这个数就一定能被11整除.
又如:判断583能不能被11整除.
用583减去11的50倍(583-11×50=33)余数是33, 33能被11整除,583也一定能被11整除.

 

 )。

人们借助电子计算机发现,在完全平方数、完全立方数中的回文数,其比例要比一般自然数中回文数所占的比例大得多。例如11^2=121,22^2=484,7^3=343,11^3=1331……都是回文数。

人们迄今未能找到四次方、五次方,以及更高次幂的回文素数。于是数学家们猜想:不存在nk(k≥4;n、k均是自然数)形式的回文数。

 

在电子计算器的实践中,还发现了一桩趣事:任何一个自然数与它的倒序数相加,所得的和再与和的倒序数相加,……如此反复进行下去,经过有限次步骤后,最后必定能得到一个回文数

原文地址:https://www.cnblogs.com/youxin/p/3234785.html