算法分析:幸运数

题目来源自庞果网:http://hero.pongo.cn/Question/Details?ID=53&ExamID=51

 

题目详情

如果一个数各个数位上的数字之和是质数,并且各个数位上的数字的平方和也是质数,则称它为幸运数。

给定x,y,求x,y之间( 包含x,y,即闭区间[x,y])有多少个幸运数。


例如1到20之间有4个幸运数,它们是11,12,14,16,像因为1+1 = 2是质数,1^2 + 1^2 = 2也是质数等等。

给定函数原型,其中1<=x<=y<=1000000000,请完成函数,实现上述功能。

答题说明
  1. 可额外编写其它的函数,然后lucky调用你编写的其它函数返回结果值;
  2. 限时3s,程序运行时间超过3s即挑战失败。

 

 

首先就能想到遍历处理[x,y]之间所有的数字,求和和平方后分别判断是不是质数,如下

        static int luckey(int x, int y)
        {
            int ret = 0;
            for (int i = x; i < y; i++)
            {
                int tmp1 = i;
                int tmp2 = i;
                int pow = 0, sum = 0;
                do
                {
                    tmp2 = tmp2 / 10;
                    int mod = tmp1 - tmp2 * 10;
                    tmp1 = tmp2;

                    sum += mod;
                    pow += mod * mod;
                } while (tmp2 > 0);

                if (IsPrime(sum) && IsPrime(pow))
                {
                    ret++;
                }
            }

            return ret;
        }


 

        static bool IsPrime(int value)
        {
            if (value % 2 == 0) return false;
            
            for (int i = 3; i < System.Math.Sqrt(value); i+=2)
            {
                if (value % i == 0) return false;
            }           

            return true;
        }


 

但很显然,这个重复计算太多,也满足不了时限。

1。 考虑到是求各数字的和以及平方和,显然数字中的0不需要计算,也就是说 11和101、110等计算出来的和

       及平方和是相同的, 所以处理的时候去掉0

2。很多数字的和或平方和是相同的, 如12和21,可以考虑先计算好一批质数,然后直接判断来避免重复计算

      (考虑到如果只是数字顺序不同的话,和及平方和结果相同,如123和312,就很容易想到集合的相同性判断

       问题,如MD5和SHA-1等伪随机数算法,但考虑到数字和及平方和范围并不大,没必要增加复杂度)

3。 如果要预先计算,就得先计算出需要计算的范围, 先要计算出最大数字y的长度,然后判断长度为y的长度

       的数字的最大可能平方和即可,如y的长度为3(如100),最大的平方和为81*3=243,也就是说我们只要

       计算出243之前的所有素数即可知道[0,999]之间所有数字的和及平方和是否是素数。

4。 预先计算结果不应该只保存质数的集合,如primes[0] = 2, primes[1] = 3;而将质数对应的index的值设为1即

       可,如primes[2] = 1,这样我们后面判断只需要查看 primes[sum]是否为1即可能知道是否为质数,

       而不需要检索数组(如桶排序)

 

代码:

        private int Luckey(int x, int y)
        {
            if (x < 11) x = 11;
            
            int ret = 0;
            int[] primes = CompPrimes(y);

            // 这里我提交的时候写成了 for (int i = x; i <= y - x; i++).................... -_-!!
            for (int i = x; i <= y; i++)
            {
                int tmp1 = i;
                int tmp2 = i;
                int pow = 0, sum = 0;                 
                do
                {
                    tmp2 = tmp2 / 10;
                    int mod = tmp1 - tmp2 * 10;
                    tmp1 = tmp2;

                    if (mod == 0) continue;

                    sum += mod;
                    pow += mod * mod;
                } while (tmp2 > 0);

                if (primes[sum] == 0 || primes[pow] == 0)
                {
                    continue;
                }

                Console.WriteLine(i);
                ret++;
            }

            return ret;
        }

        static int[] CompPrimes(int y)
        {
            int length = GetMaxLength(y);
            int[] primes = new int[length + 1];
            if (length > 2)
            {
                primes[2] = 1;
            }

            for (int i = 3; i <= length; i += 2)
            {
                bool prime = true;
                for (int j = 2; j * j <= i; j++)
                {
                    if (i % j == 0)
                    {
                        prime = false;
                        break;
                    }
                }

                if (prime) primes[i] = 1;
            }

            return primes;
        }

        static int GetMaxLength(int max)
        {
            int ret = 0;
            int tmp = max;

            while (tmp > 0)
            {
                tmp = tmp / 10;
                ret++;
            }

            return ret * 81 + 1;
        }

 

 

也可以用递归的方式去掉里面的while循环(递归的层级为y的长度)


这是我短时间能想到的最好结果了, 若更优解,望告知。
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值