我正在开发一个需要根据各种标准匹配两组数据的应用程序,包括每组中任意数量项目的总和。我把问题提炼到这个陈述:
给定一组项目和交易,找到最小的项目集合,其中总和等于最小交易集合的总和。 (这个帖子我忽略了一些复杂性,但是现在我只关心总金额匹配,而不是日期,描述,清算差异等等。)
或者,数学上:给定两组数字,找出每个总和相等的最小集合。
我遇到的其他类似的SO问题假设你提前知道了总和,或者知道你要去的每一组的数量。
这是一个测试(我认为)说明了我的目标。
[TestMethod]
public void StackOverflowTest()
{
var seta = new[]{10, 20, 30, 40, 50};
var setb = new[]{ 45, 45, 100, 200 };
var result = Magic(seta, setb);
Assert.AreEqual(new[]{40,50},result.SetA);
Assert.AreEqual(new[] { 45, 45 }, result.SetB);
}
class MagicResult
{
public int[] SetA { get; set; }
public int[] SetB { get; set; }
}
private MagicResult Magic(int[] seta, int[] setb)
{
throw new NotImplementedException();
}
我正在寻找一个优雅的解决方案,这将使这个通过,但将采取任何伪代码或建议,让我在那里;)
答案 0 :(得分:3)
蛮力:
var result = (from a in seta.Subsets()
from b in setb.Subsets()
where a.Count() > 0 && b.Count() > 0
where a.Sum() == b.Sum()
orderby a.Count() + b.Count()
select new MagicResult { SetA = a.ToArray(), SetB = b.ToArray() }
).First();
使用EvenMoreLINQ project中的子集方法。
答案 1 :(得分:2)
这可以使用O(nW)
时间内的动态编程来解决,其中W是最大总和的大小。求解两个集合的knapsack problem,为每个集合生成一个包含所有可能总和的数组,并跟踪所使用的项目数。然后,比较每个数组中的相等和,找出每个
未经测试,但这是个主意。
arr1dp = [None]*W; arr1dp[0] = 0;
arr2dp = [None]*W; arr2dp[0] = 0;
# knapsack arr1
for i in range(len(arr1)):
for cur_item in arr1:
if (arr1dp[cur_item] is not none):
arr1dp[cur_item+i] = min(arr1dp[cur_item]+1,arr1dp[cur_item])
# do the same for arr2
# omitted for brevity
# find the smallest match
for i in range(W):
if arr1dp[i] is not none and arr2dp[i] is not none:
min_val = min(min_val,arr1dp[i]+arr2dp[i])
答案 2 :(得分:1)
如果这两个集合包含一个共同的数字,则有一个大小为1的解决方案。
如果没有,请尝试两个数字的所有总和(每组中有N-choose-two或N*(N-1)/2
)。将它们与单数和两数和的集合进行比较。
如果没有喜悦,请尝试所有三个数字的总和,将它们与1,2或3数字的总和进行比较;等等,直到所有总和(一组大小为N的2 ** N)都已经尝试过。
这是工作代码,一旦找到解决方案就会停止搜索。 (可能存在具有相同数量的加数的较小总和)。它在python中,但实际上是伪代码: - )
from itertools import combinations
# To allow lists of different sizes: ensure list1 is never the short one
if len(list1) < len(list2):
list1, list2 = list2, list1
def found(val, dict1, dict2):
print "Sum:", val
print "Sum 1", dict1[val]
print "Sum 2", dict2[val]
def findsum(list1, list2):
# Each dict has sums as keys and lists of summands as values.
# We start with length 1:
dict1 = dict()
dict2 = dict()
for n in range(1, max(len(list1), len(list2))+1):
# Check all size n sums from list1 against size < n sums in list2
for nums in combinations(list1, n):
s = sum(nums)
if s in dict1: # Is this sum new for our list?
continue
dict1[s] = nums
if s in dict2:
found(s, dict1, dict2)
return # If you want to look for a smallest sum, keep going
# If list2 is too short, nothing to do
if len(list2) < n:
continue
# Check all size n sums from list2 against size <= n sums in list1
for nums in combinations(list2, n):
s = sum(nums)
if s in dict2: # Is this sum new for our list?
continue
dict2[s] = nums
if s in dict1:
found(s, dict1, dict2)
return # If you want to look for a smallest sum, keep going
findsum(list1, list2)
这是为了在最少数量的比较中找到解决方案。如果您还希望总和最小,那么在每个大小n一次生成所有n部分总和,对它们进行排序并按递增顺序检查它们。