0

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

输入

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]+" ");
        }
    }
}

请让我知道是否有更好的方法来解决这个问题(时间复杂度更低)

4

2 回答 2

1

这可以通过将其转换为子集和问题在O(n*sqrt(n))中完成。如何?

考虑所有小于或等于 N 的完全平方。此类元素的数量为 sqrt(N)。

现在的问题是我们有多少种方法可以选择这些元素的子集,使得总和= N。

这是有关此问题的讨论,您可以在此处找到类似的问题

如果通过动态规划解决问题的复杂性将是 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)))。

于 2017-08-18T21:15:23.103 回答
0

记忆化当然有助于降低这个问题的时间复杂度:

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 平方仍然需要太多时间,但这是一个开始。

于 2017-08-18T22:10:40.180 回答