有效地获取排序列表的排序总和

时间:2008-08-03 21:08:55

标签: algorithm language-agnostic

您有一个升序的数字列表,您可以想到的最有效的算法是获取该列表中每两个数字的总和的升序列表。结果列表中的重复项无关紧要,如果您愿意,可以删除它们或避免使用它们。

要清楚,我对算法很感兴趣。您可以随意使用您喜欢的任何语言和范例发布代码。

8 个答案:

答案 0 :(得分:12)

截至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],所以你只需要检查左上角的“角落”并选择最低的角落。

e.g。

  • 只有1个左上角,选2个
  • 只有1,选择5
  • 6或8,选择6
  • 7或8,选择7
  • 9或8,选择8
  • 9或9,选择两者:)
  • 10或10或10,全选
  • 12或11,选择11
  • 12或12,选择
  • 13或13,选择
  • 14或14,选择
  • 15或16,选择15
  • 只有1,选择16
  • 只有1,选择17
  • 只有1,选择18

当然,当你有批次的左上角时,这个解决方案会转移。

我很确定这个问题是Ω(n²),因为你必须计算每个M [i,j]的总和 - 除非有人有更好的求和算法:)

答案 1 :(得分:4)

不是编码出来,我想我会逐步伪代码并解释我的逻辑,以便更好的程序员可以在必要时在我的逻辑中挖洞。

在第一步,我们开始使用长度为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 

在步骤2中,因为列表是按设计排序的(为排序列表中的每个元素添加一个数字,列表仍然会被排序),我们可以通过将每个列表合并在一起而不是合并整个批次来简单地进行合并。最后这应该花费O(n ^ 2)时间。

step 2 (sumlist) 
create an empty list mergedlist
for each list templist in sumlist
   set mergelist equal to: merge(mergedlist,templist)
return mergedlist

然后合并方法将是正常的合并步骤,并进行检查以确保没有重复的总和。我不会写出来因为任何人都可以查找mergesort。

所以这是我的解决方案。整个算法的时间为O(n ^ 2)。随意指出任何错误或改进。

答案 2 :(得分:2)

您可以使用

在python中以两行完成此操作
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(see here for gory details)对已排序的X + Y矩阵的选择算法在O(kn)中执行此操作。虽然这可能是通过重复使用计算来修改它以在线生成它们,并获得一个有效的生成器。

@deuseldorf,彼得 关于(n!)有一些混乱我严重怀疑deuseldorf意味着“n阶乘”,而只是“n,(非常激动)!”

答案 3 :(得分:1)

无论你做什么,没有输入值的额外限制,你都不能做得比O(n ^ 2)更好,因为你必须遍历所有数字对。迭代将主导排序(您可以在O(n log n)或更快)中进行排序。

答案 4 :(得分:1)

在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);
}

答案 5 :(得分:1)

我能想到的最好的方法是生成每对的和的矩阵,然后将这些行合并在一起,进行合并排序。我觉得我错过了一些简单的见解,可以揭示更有效的解决方案。

我的算法,在Haskell中:

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 []”,因为它更快。

答案 6 :(得分:1)

这个问题一直困扰着我的大脑一天。真棒。

无论如何,你不能轻易地摆脱它的n ^ 2性质,但你可以用合并稍微好一些,因为你可以绑定范围来插入每个元素。

如果查看生成的所有列表,它们将具有以下形式:

(a[i], a[j]) | j>=i

如果你翻转90度,你会得到:

(a[i], a[j]) | i<=j

现在,合并过程应该采用两个列表ii+1(对应于第一个成员始终为a[i]a[i+1]的列表),您可以绑定范围,以(a[i + 1], a[j])的位置和i的位置将元素(a[i], a[j])插入列表(a[i + 1], a[j + 1])

这意味着您应该以{{1​​}}方式反向合并。我还不知道你是否可以在j之间利用这一点,但似乎有可能。

答案 7 :(得分:-4)

如果您正在寻找一种真正与语言无关的解决方案,那么您会对我的观点感到非常失望,因为您将陷入for循环和一些条件限制。但是如果你把它打开到函数式语言或函数式语言特性(我正在看你LINQ)那么我的同事可以在Ruby,Lisp,Erlang和其他人的优秀例子中填充这个页面。