题目来源自庞果网: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,请完成函数,实现上述功能。
- 可额外编写其它的函数,然后lucky调用你编写的其它函数返回结果值;
- 限时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的长度)
这是我短时间能想到的最好结果了, 若更优解,望告知。