像这样阅读您的问题:对于您的随机整数数组,找到一组(或所有)具有给定总和的整数。
这是一个 NP 完全问题 - 即没有已知的算法可以有效地解决它。
已知最快的方法相当复杂,因此我们将采用一个简单的解决方案 - 如果您不是在每一帧上都这样做或者输入集很大,那么应该就足够了。
这也应该适用于输入集中的 0 或负值。
// The sum we're looking for:
var requiredSum:int = 8;
// Our input set:
var numberArray:Array = [1, 2, 3, 4, 5, 2, 3];
// Results will be stored here:
var resultSets:Array = [];
// Go through all possible subset sizes.
// This allows subset sizes all the way up to the size of
// the input set (numberArray.length).
// You can modify it to a fixed value (say, 5), of course:
for (var subsetSize:int = 1; subsetSize <= numberArray.length; subsetSize++)
{
// We'll use the same array for all our attempts of this size:
var subset:Array = new Array(subsetSize);
findSum(numberArray, subset, 0, 0);
}
// Output results:
for (var i:int = 0; i < resultSets.length; i++)
{
trace(resultSets[i].join("+"));
}
// numberArray : Our input set
// subset : The set we're currently filling
// setIndex : The position we're at in numberArray
// subsetIndex : The position we're at in the set we're filling
function findSum(numberArray:Array, subset:Array, setIndex:int,
subsetIndex:int):void
{
// Try every value from the input set starting from our current position,
// and insert the value at the current subset index:
for (var index:int = setIndex ; index < numberArray.length; index++)
{
subset[subsetIndex] = numberArray[index];
// Have we filled the subset?
if (subsetIndex == subset.length - 1)
{
var sum:int = 0;
for (var i:int = 0; i < subset.length; i++)
{
sum += subset[i];
}
if (sum == requiredSum)
{
// Clone the array before adding it to our results,
// since we'll be modifying it if we find more:
resultSets.push(subset.concat());
}
}
else
{
// Recursion takes care of combining our subset so far
// with every possible value for the remaining subset indices:
findSum(numberArray, subset, index + 1, subsetIndex + 1);
}
}
}
上述代码中使用的值的输出:
3+5
5+3
1+2+5
1+3+4
1+4+3
1+5+2
2+3+3
2+4+2
3+2+3
1+2+3+2
1+2+2+3
如果我们只需要知道是否存在总和,则不需要结果集——我们只返回真/假,并在找到总和时完全退出递归算法:
var requiredSum:int = 8;
var numberArray:Array = [1, 2, 3, 4, 5, 2, 3];
// Go through all possible subset sizes:
for (var subsetSize:int = 1; subsetSize <= numberArray.length; subsetSize++)
{
// We'll use the same array for all our attempts of this size:
var subset:Array = new Array(subsetSize);
if (findSum(numberArray, subset, 0, 0))
{
trace("Found our sum!");
// If we found our sum, no need to look for more sets:
break;
}
}
// numberArray : Our input set
// subset : The set we're currently filling
// setIndex : The position we're at in numberArray
// subsetIndex : The position we're at in the set we're filling
// RETURNS : True if the required sum was found, otherwise false.
function findSum(numberArray:Array, subset:Array, setIndex:int,
subsetIndex:int):Boolean
{
// Try every value from the input set starting from our current position,
// and insert the value at the current subset index:
for (var index:int = setIndex ; index < numberArray.length; index++)
{
subset[subsetIndex] = numberArray[index];
// Have we filled the subset?
if (subsetIndex == subset.length - 1)
{
var sum:int = 0;
for (var i:int = 0; i < subset.length; i++)
{
sum += subset[i];
}
// Return true if we found our sum, false if not:
return sum == requiredSum;
}
else
{
if (findSum(numberArray, subset, index + 1, subsetIndex + 1))
{
// If the "inner" findSum found a sum, we're done, so return
// - otherwise stay in the loop and keep looking:
return true;
}
}
}
// We found no subset with our required sum this time around:
return false;
}
ETA:这是如何工作的......如前所述,这是一个简单的解决方案 - 换句话说,我们只是检查 的每个排列numberArray
,对每个排列求和,并检查它是否是我们想要的总和。
最复杂的部分是进行所有排列。这段代码的方式是通过递归——即,findSum()
函数填充一个槽然后调用自己来填充下一个槽,直到所有槽都被填满并且它可以检查总和。我们将在numberArray
这里使用 [1, 5, 4, 2] 作为示例:
- 在一个循环中遍历所有子集大小 - 即,首先制作所有 [a],然后是所有 [a,b]、[a,b,c]、[a,b,c,d]... 等等。
- 对于每个子集大小:
- 填充
1
子集的插槽...
- ... numberArray 的每个值 - [ 1 , ?, ?], [5, ?, ?], [4, ?, ?] ...
- 如果子集中的所有槽都已填满,请检查总和是否匹配并跳过步骤 4。
- (递归)调用
findSum
:
- 填充
2
子集的插槽...
- ... numberArray 的每个剩余值 - [1, 5 , ?], [1, 4, ?], [1, 2, ?]
- 如果子集中的所有槽都已填满,请检查总和是否匹配并跳过步骤 4。
- (递归)调用
findSum
:
3
子集的填充槽
- ... numberArray 的每个剩余值 - [1, 5, 4 ], [1, 5, 2]
- 如果子集中的所有槽都已填满,请检查总和是否匹配并跳过步骤 4。
- (递归)调用
findSum
(这会“永远”进行,或者直到所有插槽都被填满并且我们“跳过第 4 步”)
- 转至 2.4.4.1。尝试下一个值 slot
3
。
- 转到 2.4.1 尝试下一个值 slot
2
。
- 转到 2.1 尝试下一个值 slot
1
。
这样,我们遍历大小为 1、2、3、4 的每个排列...
这里可以进行更多优化,因为代码从不检查输入集中实际上是否有足够的值来填充剩余的插槽——即它会执行一些循环并且findSum()
不需要对其进行调用。然而,这只是效率问题 - 结果仍然是正确的。