要明确一点,我对算法感兴趣。可以自由使用任何语言和范式发布代码。
截至2018年修改:你应该停止阅读这篇文章。(但我不能删除它,因为它已经被接受。)
如果你像这样写出总和:
1 4 5 6 8 9
---------------
2 5 6 7 9 10
8 9 10 12 13
10 11 13 14
12 14 15
16 17
18
注意到M[i,j] <= M[i,j+1]且M[i,j] <= M[i+1,j],因此您只需要检查左上角并选择最小值。
例如:
当您有很多左上角时,这种解决方案会退化。
我非常确定这个问题是Ω(n²),因为您必须为每个M[i,j]计算总和--除非有人有更好的求和算法:)
我觉得不需要编写代码,而是用伪代码逐步解释我的逻辑,这样更好的程序员可以在必要时指出我的逻辑漏洞。
第一步,我们从一个长度为n的数字列表开始。对于每个数字,我们需要创建一个长度为n-1的列表,因为我们不会将一个数字加到它自己上面。最终,我们生成了大约n个排序列表,用了O(n^2)的时间。
step 1 (startinglist)
for each number num1 in startinglist
for each number num2 in startinglist
add num1 plus num2 into templist
add templist to sumlist
return sumlist
step 2 (sumlist)
create an empty list mergedlist
for each list templist in sumlist
set mergelist equal to: merge(mergedlist,templist)
return mergedlist
allSums = set(a+b for a in X for b in X)
allSums = sorted(allSums)
n^2
(可能还有一个对于集合的额外对数因子?)用于迭代和s * log(s)用于排序,其中s是集合的大小。
例如,如果X = [1,2,4,...,2^n]
,那么集合的大小可能会达到n*(n-1)/2
。因此,如果您想生成此列表,则最坏情况下至少需要n^2/2
,因为这是输出的大小。
但是,如果您要选择结果的前k个元素,则可以使用Frederickson和Johnson的排序X+Y
矩阵的选择算法在O(kn)内完成(有关详细信息,请参见这里)。尽管可能可以通过重复使用计算并获得此集合的高效生成器来修改它们以在线生成。
@deuseldorf,Peter
(n!)
存在一些混淆,我严重怀疑deuseldorf的意思不是“n阶乘”,而只是“n,(非常兴奋)!”
matrixOfSums list = [[a+b | b <- list, b >= a] | a <- list]
sortedSums = foldl merge [] matrixOfSums
--A normal merge, save that we remove duplicates
merge xs [] = xs
merge [] ys = ys
merge (x:xs) (y:ys) = case compare x y of
LT -> x:(merge xs (y:ys))
EQ -> x:(merge xs (dropWhile (==x) ys))
GT -> y:(merge (x:xs) ys)
我发现了一个小改进,更适合于惰性流式编程。不要一对一对地合并列,而是一次性合并所有列。优点是您可以立即开始获取列表的元素。
-- wide-merge does a standard merge (ala merge-sort) across an arbitrary number of lists
-- wideNubMerge does this while eliminating duplicates
wideNubMerge :: Ord a => [[a]] -> [a]
wideNubMerge ls = wideNubMerge1 $ filter (/= []) ls
wideNubMerge1 [] = []
wideNubMerge1 ls = mini:(wideNubMerge rest)
where mini = minimum $ map head ls
rest = map (dropWhile (== mini)) ls
betterSortedSums = wideNubMerge matrixOfSums
然而,如果你知道你将使用所有的总和,并且提前获取其中一些没有优势,那么选择 'foldl merge []
',因为它更快。
在SQL中:
create table numbers(n int not null)
insert into numbers(n) values(1),(1), (2), (2), (3), (4)
select distinct num1.n+num2.n sum2n
from numbers num1
inner join numbers num2
on num1.n<>num2.n
order by sum2n
C# LINQ:
List<int> num = new List<int>{ 1, 1, 2, 2, 3, 4};
var uNum = num.Distinct().ToList();
var sums=(from num1 in uNum
from num2 in uNum
where num1!=num2
select num1+num2).Distinct();
foreach (var s in sums)
{
Console.WriteLine(s);
}
这个问题已经困扰了我一天了。太棒了。
无论如何,很难摆脱它的n^2本质,但是由于您可以将要插入每个元素的范围限定在合并中,因此可以略微改善。
如果您查看您生成的所有列表,则具有以下形式:
(a[i], a[j]) | j>=i
如果将其旋转90度,则变为:
(a[i], a[j]) | i<=j
现在,合并过程应该取两个列表i
和i+1
(这对应于第一个成员始终为a[i]
和a[i + 1]
的列表),您可以通过 (a[i], a[j])
的位置和(a[i + 1], a[j + 1])
的位置来限定将元素(a[i + 1], a[j])
插入到列表i
的范围。
j
的相反顺序合并。我还不知道是否可以在跨越 j
方面利用它,但似乎有可能。如果您正在寻找一种真正的语言无关解决方案,那么据我所知,您将感到非常失望,因为您只能使用for循环和一些条件语句。但是,如果您将其扩展到函数式语言或函数式语言特性(我在看你,LINQ),那么我的同事们可以用Ruby、Lisp、Erlang等优雅的示例填充此页面。