在整数数组中查找元素,使得它们的和为X,并且它们的平方和最小

时间:2014-09-15 02:00:20

标签: algorithm recursion data-structures

给定长度为arr的数组n,找到arr中的任何元素,使其总和为x,其平方和最小。我试图找到复杂度最低的算法。到目前为止,我已经编写了一个简单的递归算法,该算法可以找到数组中的所有子集,并将总和检查作为基本条件。我写的代码是javascript,如下所示:

var arr = [3, 4, 2, 1];
var arr2 = arr.map(function(n) { return n*n; });

var max_sum = 5;
var most_min = -1;

function _rec(i, _sum, _square) {
    if(_sum >= max_sum) {
        if(most_min == -1 || _square < most_min) {
            most_min = _square;
            console.log("MIN: " + most_min);
        }
        console.log("END");
        return;
    }

    if(i >= arr.length)
        return;

    console.log(i);
    var n = arr[i];
    // square of above number
    var n2 = arr2[i];    
    _sum = _sum + n;
    _square = _square + n2;
    _rec(i+1, _sum, _square);

    _sum = _sum - n;
    _square = _square - n2;
    _rec(i+1, _sum, _square);
}

_rec(0, 0, 0);

访问http://jsfiddle.net/1dxgq6d5/6/以查看上述算法的输出。上面的算法非常简单,它通过在每个递归步骤中评估两个选项来查找所有子集; 1)选择当前数字或2)拒绝,然后进行递归。

我试图找到一种比上面的简单递归更有效的算法。任何建议或帮助将不胜感激。

还有一个假设

我认为如果我对数组进行排序,并找到方差最小的元素子集(彼此之间的分隔),使得它们的总和为x将满足我的要求。不确定,如果这将是非常有用的,但我目前正在努力改善我目前的盲目递归方法。

3 个答案:

答案 0 :(得分:1)

首先,您要查找子集,而不是排列,因为您不关心每个集合中元素的顺序。

其次,即使不试图最小化平方和,只要找出是否有一个与目标数相加的子集就是NP完全 - 这就是subset sum problem。目前大多数计算机科学家认为P!= NP,因此没有有效的(多项式时间)算法。

答案 1 :(得分:1)

子集和仅仅是NP难的,因此可以通过动态编程获得有效的解决方案(假设输入数组由具有相对较小总和的整数组成)。通过存储数组中前k个元素的可能性,从递归和深度优先尝试所有可能性切换到迭代和广度优先尝试所有可能性。在考虑元素k + 1之前,通过丢弃除了可以产生的每个总数的最小平方和之外的所有数据来过滤该数组。

答案 2 :(得分:1)

我以比简单递归更有效的方式解决了问题。我使用动态编程方法。下面是我写的python代码:

_sum=7
_set=[1,1,2,3,4,6]

current_idx = 0

sum_mapping = [[-1 for i in range(len(_set) + 1)] for i in range(_sum)]
max_sum = _set[current_idx]
for i in range(0, _sum):
    current_sum = i + 1
    for j in [i for i in range(0, current_idx+1)][::-1] + \
        [i for i in range(current_idx + 1, len(_set))]:
        required_value = current_sum - _set[j]
        if required_value < 0:
            break
        if required_value == 0 or sum_mapping[required_value - 1][j] != -1:
            _j = j + 1
            sum_mapping[i][_j:] = [j]*(len(_set) - j)
            break
    if max_sum == current_sum:
        current_idx = current_idx + 1
        max_sum = max_sum + _set[current_idx]

_cur = sum_mapping[_sum-1][len(_set)]

if _cur != -1:
    _l_sum = _sum
    while _l_sum != 0:
        print(_set[_cur])
        _l_sum = _l_sum - _set[_cur]
        _cur = sum_mapping[_l_sum -1][len(_set)]

这是ideone输出:http://ideone.com/OgGN2f