【问题标题】:Which algorithm is faster to sort these number pairs?哪种算法对这些数字对进行排序更快?
【发布时间】:2017-11-19 10:34:07
【问题描述】:

我在 python 中编写了两个解决方案。它应该获取一个数字列表并对相加为和的数字进行排序,这两个都返回相同的对,但哪个更有效?我不确定使用 python 的 count 方法是否在幕后做更多的工作,使第二个更长

numbers =  [1, 2, 4, 4, 4, 4, 5, 7, 7, 8, 8, 8, 9]

match = []
for i in range(len(numbers)):
    for j in range(len(numbers)):
        if (i!=j):
            if(numbers[i] + numbers[j] == sum):
                match.append([numbers[i], numbers[j]])


match2 = []

for i in range(len(numbers)):
    counterPart = abs(numbers[i] - sum)

    numberOfCounterParts = numbers.count(counterPart)

    if(numberOfCounterParts >= 1):
        if(counterPart == numbers[i]):
            for j in range(numbers.count(counterPart)-1):
                match2.append([numbers[i], counterPart])
        else:
            for j in range(numbers.count(counterPart)):
                match2.append([numbers[i], counterPart])

我还缺少更好的解决方案吗?

【问题讨论】:

  • 老实说,它们都不是有效的解决方案,使用字典是要走的路。你的算法是 O(n^2)。
  • @Ding 您是否建议以编程方式将列表转换为字典?使键成为索引?
  • 循环遍历列表,对于每个数字 x,使用 sum-x 作为键,x 作为值。这样,您只需浏览列表一次。
  • @Ding 你确定这是我应该采取的方法吗?因为我做了一些搜索,发现了这个stackoverflow.com/questions/3420937/… 哪个最佳答案使用了不使用字典的递归函数

标签: python algorithm performance sorting search


【解决方案1】:

是的,如果您知道数据的lower_boundupper_bound,可以使用更好的算法。 Counting Sort 占用O(N) 时间和空间不是恒定的(取决于上限和下限的范围)。

参考Counting Sort

PS:计数排序不是基于比较的排序算法。

参考下面的示例代码:

def counting_sort(numbers, k):
    counter = [0] * (k + 1)
    for i in numbers:
        counter[i] += 1

    ndx = 0
    for i in range(len(counter)):
        while 0 < counter[i]:
            numbers[ndx] = i
            ndx += 1
            counter[i] -= 1

【讨论】:

  • 您能否编辑您的答案以使用可点击的链接而不是代码块中的网址?
  • 如何以 O(N) 形式将计数排序应用于此问题?
  • @barny 数字在 [1-9] 范围内
  • 什么?我问:请编辑您的答案,并准确解释您链接到的计数排序如何应用于这个特定问题并实现 O(N) 性能?请记住问题上的问题,即通常可以产生 O(N^2) 结果的东西不能通常在 O(N) 时间内运行。
  • 该代码显示了计数排序,但没有显示如何在所提出的问题上实现
【解决方案2】:

您可以使用timeit 模块自行运行测试:

t1 = timeit(setup='from __main__ import sort1, numbers',
            stmt='sort1(numbers)',
            number=1)
t2 = timeit(setup='from __main__ import sort2, numbers',
           stmt='sort2(numbers)',
           number=1)

print(t1)
print(t2)

还要注意sum 是一个内置变量,因此不是一个好的变量名称...

有更好的算法!特别是考虑到您的列表中有重复项。


这里有一个更快的版本,它只会给你匹配而不是匹配的多样性:

def sum_target(lst, target):
    # make list unique
    unique_set = set(lst)
    unique_list = list(unique_set)

    remainders = {number: target-number for number in unique_list}
    print(remainders)

    match = set()
    for a, b in remainders.items():
        if a == b and lst.count(a) >= 2:
            match.add((a, b))
        else:
            if b in remainders:
                match.add(frozenset((a, b)))

    return match

【讨论】:

  • 但是我也需要重复值。所以让它独一无二不是我想要的。有些人建议使用字典
  • 在获得总和为目标值的对后恢复多重性既快速又简单。
  • 难道你不需要再做一次搜索看看它出现了多少次吗?
  • 这种方法行不通!对不起。如果目标是 12 并且列表中有两个 6,算法将找不到它......我会再看看它。
【解决方案3】:

比较算法时,您应该比较它们的时间复杂度。测量时间也是一个好主意,但严重依赖于输入,现在输入很少。


第一个算法采用:

O(N2)

因为双 for 循环。

对于第二种算法,您应该考虑到count() 的时间复杂度为O(N)。你有一个 for 循环,在它的主体中 count() 将被调用两次,一次在 abs() 之后,一次在你进入的 if-else 语句的任何主体中。结果时间复杂度为O(N) * 2 * O(N) = 2*O(N&lt;sup&gt;2&lt;/sup&gt;),产生:

O(N2)

这意味着两种算法具有相同的时间复杂度。因此,现在通过运行许多实验并取时间测量的平均值来衡量性能是有意义的,需要足够大的输入来反映性能。

【讨论】:

  • 好吧,我想通了.. 那我怎么写一个更快的算法呢?字典不起作用,因为我需要检查重复值
  • @joe 这是另一个问题。接受已经发布的答案,如果需要,发布一个新问题,或者更好地在代码审查中发布。 ;)
【解决方案4】:

衡量算法的复杂性几乎总是有用的。

您的两种算法都具有 O(N^2) 复杂度,因此在性能方面几乎可以互换。

您可以通过保留值-索引对的映射来改进您的算法。它将复杂度降低到 O(N),基本上你会有一个循环。

【讨论】:

  • 但是我将无法比较多个重复值
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-17
  • 2017-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-29
  • 2020-02-16
相关资源
最近更新 更多