所有可能的不同完美正方形的总和等于给定数字的平方

时间:2017-08-18 19:35:38

标签: algorithm math recursion perfect-square

我正在尝试编写一个程序来解决一个问题,该问题说明如下 "打印所有可能的不同的完美正方形,其总和等于给定数字的平方"。 例如 -

输入

11

输出

1 4 16 36 64

1 4 16 100

4 36 81

我尝试了基本的递归方法,我的代码传递了小输入。当我尝试像116这样更大的数字时,它会永远运行。我的JAVA代码

public class SumOfPerfectSquare {
    public static void main(String args[]){
        generateSum(11);
    }

    private static void generateSum(int num) {
        int arr[] = new int[num];
        for(int i=1; i<num; i++){
            arr[i] = i*i;
        }
        findSum(arr, num*num, 1, 0, "");
    }

    private static void findSum(int arr[], int desiredSqr, int pointer, int currentSum, String sumStr) {
        if(pointer == arr.length || currentSum >= desiredSqr){
            if(currentSum == desiredSqr){
                System.out.println(sumStr);
            }
            return;
        }
        for(int i=pointer; i<arr.length; i++){
            findSum(arr, desiredSqr, i+1, currentSum+arr[i], sumStr+arr[i]+" ");
        }
    }
}

如果有更好的解决方法(时间复杂度较低),请告知我们

2 个答案:

答案 0 :(得分:0)

这可以通过将其转换为subset sum problem O(n * sqrt(n))中完成。怎么样?

考虑所有小于或等于N的完美正方形。这些元素的数量为sqrt(N)。

现在问题在于我们可以通过多少种方式选择这些元素的子集,使得sum = N

这是关于此问题的discussion,您可以在此处找到similar questions

如果通过动态编程解决问题的复杂性将是O(n * sqrt(n))

打印所有这些组合将具有 O(sqrt(n)* 2 ^(sqrt(n)))复杂度,因为它们是可能的2 ^(sqrt(n))子集。我们必须检查这个子集是否有sum = N.

现在,如果我们遍历从1到2 ^(srtn(N)-1)的所有数字。这个数字可以表示所有子集,它将是这个数字的设置位的索引。遍历此数字将花费O(sqrt(N))时间。

总体复杂度为O(sqrt(N)* 2 ^(sqrt(N)))。

答案 1 :(得分:0)

Memoization肯定有助于减少此问题的时间复杂性:

const memoize = callback => {
    const memo = new Map;

    return function () {
        const length = arguments.length, last = length - 1;

        let map = memo;

        for (let i = 0; i < last; i++) {
            const argument = arguments[i];
            if (!map.has(argument)) map.set(argument, new Map);
            map = map.get(argument);
        }

        const argument = arguments[last];
        if (!map.has(argument)) map.set(argument, callback.apply(null, arguments));
        return map.get(argument);
    };
};

const generateSums = memoize((sum, xs, index) => {
    if (sum === 0) return [[]];

    const result = [], length = xs.length;

    for (let i = index; i < length; i++) {
        const x = xs[i], diff = sum - x;
        let j = i + 1; while (xs[j] > diff) j++;
        const xss = generateSums(diff, xs, j);
        for (const xs of xss) result.push([x].concat(xs));
    }

    return result;
});

const solve = n => {
    const xs = [];
    for (let x = n - 1; x > 0; x--) xs.push(x * x);
    return generateSums(n * n, xs, 0);
};

console.time("Generate sums for 50²");
console.log(solve(50).length);
console.timeEnd("Generate sums for 50²");

如果没有记忆,则需要更长的时间(注意,它可能会使浏览器崩溃):

const generateSums = (sum, xs, index) => {
    if (sum === 0) return [[]];

    const result = [], length = xs.length;

    for (let i = index; i < length; i++) {
        const x = xs[i], diff = sum - x;
        let j = i + 1; while (xs[j] > diff) j++;
        const xss = generateSums(diff, xs, j);
        for (const xs of xss) result.push([x].concat(xs));
    }

    return result;
};

const solve = n => {
    const xs = [];
    for (let x = n - 1; x > 0; x--) xs.push(x * x);
    return generateSums(n * n, xs, 0);
};

console.time("Generate sums for 50²");
console.log(solve(50).length);
console.timeEnd("Generate sums for 50²");

解决116平方仍然需要太多时间,但这是一个开始。