给定数N,N可以表示为两个或更多连续完美正方形的总和吗?

时间:2015-04-09 21:02:40

标签: java algorithm math

在我最近的一次计算机编程竞赛中,有一个问题,你必须确定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并继续评估从下限到上限的平方和。问题是只有下限发生变化,而上限保持不变。

10 个答案:

答案 0 :(得分:2)

这应该是一种有效的算法,用于确定它是否是连续数字的平方和。

  1. 以1的下限和上限开始。当前的平方和为1

    public static boolean isSumOfSquares(int num) {
        int sum = 1;
        int lowerBound = 1;
        int upperBound = 1;
    
  2. 最大可能上限是其平方小于或等于要测试的数字的最大数。

    int max = (int) Math.floor(Math.sqrt(num));
    
  3. 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;
    
  4. 测试5111354181595。是的,其中一些不是回文,但我只是测试连续数字部分的平方和。

    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)

  1. 因为只有很少的整数幂,你可以创建一个幂数组​​。
  2. 然后你可以拥有第一个和最后一个包含的索引。最初他们都是1.
  3. 虽然总和低于您的数字,但增加最后包含的索引。更新总和
  4. 虽然总和更高,但增加第1个包含指数。更新总和
  5. 或没有任何数组,如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。