在我最近的一次计算机编程竞赛中,有一个问题,你必须确定1 <= N <= 1000的数字N是否是回文平方。回文正方形是可以向前和向后读取相同的数字,并且可以表示为两个或更多个连续正方形的总和。例如,595是回文并且可以表示为6 ^ 2 + 7 ^ 2 + 8 ^ 2 + 9 ^ 2 + 10 ^ 2 + 11 ^ 2 + 12 ^ 2。
我理解如何确定这个数字是不是回文,但是我无法弄清楚它是否可以表示为两个或更多个连续方格的总和。
这是我尝试过的算法:
public static boolean isSumOfSquares(int num) {
int sum = 0;
int lowerBound = 1;
//largest square root that is less than num
int upperBound = (int)Math.floor(Math.sqrt(num));
while(lowerBound != upperBound) {
for(int x=lowerBound; x<upperBound; x++) {
sum += x*x;
}
if(sum != num) {
lowerBound++;
}
else {
return true;
}
sum=0;
}
return false;
}
我的方法将上边界设置为与数字最接近的平方根,并将下限设置为1并继续评估从下限到上限的平方和。问题是只有下限发生变化,而上限保持不变。
答案 0 :(得分:2)
这应该是一种有效的算法,用于确定它是否是连续数字的平方和。
以1的下限和上限开始。当前的平方和为1
。
public static boolean isSumOfSquares(int num) {
int sum = 1;
int lowerBound = 1;
int upperBound = 1;
最大可能上限是其平方小于或等于要测试的数字的最大数。
int max = (int) Math.floor(Math.sqrt(num));
while循环。如果平方和太小,则添加下一个方块,递增upperBound
。如果平方和太高,则减去第一个方块,递增lowerBound
。如果找到号码则退出。如果它不能表示为连续数字的平方和,则最终upperBound
将超过max
,并返回false
。
while(sum != num)
{
if (sum < num)
{
upperBound++;
sum += upperBound * upperBound;
}
else if (sum > num)
{
sum -= lowerBound * lowerBound;
lowerBound++;
}
if (upperBound > max)
return false;
}
return true;
测试5
,11
,13
,54
,181
和595
。是的,其中一些不是回文,但我只是测试连续数字部分的平方和。
1: true
2: false
3: false
4: true
5: true
11: false
13: true
54: true
180: false
181: true
595: true
596: false
答案 1 :(得分:1)
只是为了玩,我创建了一个Javascript函数,它可以获得最小值和最大值之间的所有回文正方形:http://jsfiddle.net/n5uby1wd/2/
HTML
<button text="click me" onclick="findPalindromicSquares()">Click Me</button>
<div id="test"></div>
JS
function isPalindrome(val) {
return ((val+"") == (val+"").split("").reverse().join(""));
}
function findPalindromicSquares() {
var max = 1000;
var min = 1;
var list = [];
var done = false,
first = true,
sum = 0,
maxsqrt = Math.floor(Math.sqrt(max)),
sumlist = [];
for(var i = min; i <= max; i++) {
if (isPalindrome(i)) {
done = false;
//Start walking up the number list
for (var j = 1; j <= maxsqrt; j++) {
first = true;
sum = 0;
sumlist = [];
for(var k = j; k <= maxsqrt; k++) {
sumlist.push(k);
sum = sum + (k * k);
if (!first && sum == i) {
list.push({"Value":i,"Sums":sumlist});
done = true;
}
else if (!first && sum > i) {
break;
}
first = false;
if (done) break;
}
if (done) break;
}
}
}
//write the list
var html = "";
for(var l = 0; l < list.length; l++) {
html += JSON.stringify(list[l]) + "<br>";
}
document.getElementById("test").innerHTML = html;
}
如果min = 1且max = 1000,则返回:
{"Value":5,"Sums":[1,2]}
{"Value":55,"Sums":[1,2,3,4,5]}
{"Value":77,"Sums":[4,5,6]}
{"Value":181,"Sums":[9,10]}
{"Value":313,"Sums":[12,13]}
{"Value":434,"Sums":[11,12,13]}
{"Value":505,"Sums":[2,3,4,5,6,7,8,9,10,11]}
{"Value":545,"Sums":[16,17]}
{"Value":595,"Sums":[6,7,8,9,10,11,12]}
{"Value":636,"Sums":[4,5,6,7,8,9,10,11,12]}
{"Value":818,"Sums":[2,3,4,5,6,7,8,9,10,11,12,13]}
允许测试单个值的更新版本:http://jsfiddle.net/n5uby1wd/3/ 只花了几秒钟才发现它们都在1到1,000,000之间。
答案 2 :(得分:1)
你正在寻找S(n,k)= n ^ 2 +(n + 1)^ 2 +(n + 2)^ 2 + ...(n +(k - 1))^ 2达到指定的总和m,即S(n,k)= m。 (我假设您将单独测试回文。)S(n,k) - m是n的二次方。您可以轻松地计算出S(n,k) - m的显式表达式,因此使用二次公式求解它。如果S(n,k) - m具有正整数根,则保留该根;它为您的问题提供了解决方案。
我假设您可以轻松测试二次方是否具有正整数根。困难的部分可能是确定判别式是否具有整数平方根;我猜你可以搞清楚。
你必须寻找k = 2,3,4 ......你可以在1 + 4 + 9 + ... + k ^ 2&gt;时停止。米你可以为此制定一个明确的表达式。
答案 3 :(得分:0)
我能想到的一种方法(可能效率不高)是,
假设N是90.
X = 9 (sqrt的整数值为90)
1.创建一个包含小于x [1,4,9,16,25,36,49,64,81]的所有整数幂的数组
2。使用递归生成数组中项目的所有可能组合。 [1,4],[1,9],[1,16],... [4,1],[4,9],...,[1,4,9] .... <登记/> 3。对于每个组合(当您生成时) - 检查总和是否加起来
为了节省内存空间,在生成每个实例时,您可以验证它是否总和为N.如果不是,则丢弃它并继续下一个实例。
其中一个例子是[9,81],其中9 + 81 = [90]
答案 4 :(得分:0)
或没有任何数组,如rgettman的回答
答案 5 :(得分:0)
从第一个完美正方形的阵列开始,让我们说你的数字是13和17,然后你的数组将包含:1,4,9和16
进行这种检查:
13减1(0 ^ 2)是12. 1是完美的正方形,12不是。
13减2(1 ^ 2)是11. 2是完美的正方形,11不是。
13减4(2 ^ 2)是9. 4是完美的正方形,9是完美的正方形,所以 13是两个完美的总和
17减去1是16. 1和16 是完美正方形。消除选择。
继续前进,直到找到一个不是两个完美正方形的总和。
答案 6 :(得分:0)
我认为您可以通过以下方式快速确定数字是否是连续正方形的总和,这极大地减少了需要完成的算术量。首先,预先计算所有正方形的总和并将它们放在一个数组中:
0, 0+1=1, 1+4=5, 5+9=14, 14+16=30, 30+25=55, 55+36=91, ...
现在,如果一个数字是两个或多个连续方格的总和,我们可以通过从上面的序列中添加一个数字来完成它,以获得上述序列中的另一个数字。例如,77 = 16 + 25 + 36,我们可以通过添加列出的数字14 = 0 + 1 + 4 + 9来完成它以获得列出的数字91 = 14 + 77 =(0 + 1 + 4 + 9) +(16 + 25 + 36)。如果列出的两个数字在列表中至少有两个位置,则反之亦然。
我们的名单需要多长时间?我们可以在添加满足n
的{{1}}的第一个方格时停止,其中(n-1)^2+n^2 > max
在这种情况下为1000.简化,我们可以在max
或{{1}时停止}。因此,对于2(n-1)^2 > max
,我们可以在n > sqrt(max/2) + 1
时停止。
要快速测试集合中的成员资格,我们应该对列表中的数字进行哈希处理,并将它们存储在列表中;散列的值应该是列表中数字的位置,这样我们就可以快速找到它的位置,以确定它是否至少离起点两个位置。
这是我在Java中的建议:
max=1000
该函数的每次运行最多执行25次添加和25次哈希表查找。没有乘法。
要有效地使用它来解决问题,构建1,2和3位数的回文(1位数很容易:1,2,...,9; 2位乘以11:11,22 ,33,...,99;通过公式i * 101 + j * 10的3位数。然后用上面的函数检查回文,如果它返回true则打印出来。
答案 7 :(得分:-1)
public static boolean isSumOfSquares(int num) {
int sum = 0;
int lowerBound = 1;
//largest square root that is less than num
int upperBound = (int)Math.floor(Math.sqrt(num));
while(lowerBound != upperBound) {
sum = 0
for(int x=lowerBound; x<upperBound; x++) {
sum += x * x;
}
if(sum != num) {
lowerBound++;
}
else {
return true;
}
}
return false;
}
答案 8 :(得分:-1)
也许我错过了这一点,但考虑到N,对于1&lt; = N&lt; = 1000,最有效的方法是以某种方式解决问题(可能是暴力)并将解决方案存储在交换机中。
switch(n){
case 5:
case 13:
...
return true;
default:
return false;
}
答案 9 :(得分:-1)
public static boolean validNumber(int num) {
if (!isPalindrome(num))
return false;
int i = 1, j = 2, sum = 1*1 + 2*2;
while (i < j)
if (sum > num) {
sum = sum - i*i; i = i + 1;
} else if (sum < num) {
j = j + 1; sum = sum + j*j;
} else {
return true;
}
return false;
}
然而,只有十一个“好数字”{5,55,77,181,313,434,505,545,595,636,818}。这种情况非常缓慢,N = 10 ^ 6,只有59。