从两个列表中选择最佳组合的算法

时间:2014-07-29 08:56:09

标签: algorithm sorting optimization combinations

我有双向飞行的搜索结果。因此,有两个列表包含出发航班和抵达航班,例如:

  • 出发航班列表有20个航班。
  • 抵达航班列表有30个航班

所以,我将在出发航班和到达航班之间有600(20 * 30)组合。我将组合列表称为结果列表

但是,我只想从600组合中选择限制。例如,我将选择最佳的100个航班组合。结合航班的标准是出发和到达航班的便宜价格。

为此,我将按出发和到达航班的总价格对结果列表进行排序。然后我从结果列表中获取前100个元素以获得我想要的内容。

但是,如果出发航班列表有200个航班,并且到达航班列表有300个航班,我将拥有结果列表,有60.000个元素。因此,我将对包含60.000个元素的列表进行排序,以找到最佳的100个元素。

因此,有任何算法可以选择最佳组合作为我的情况。

非常感谢你。

2 个答案:

答案 0 :(得分:4)

您的问题并非100%明确,但我了解您正在寻找更快的算法来查找一定数量的最佳/最便宜的出发和到达航班组合。

您可以通过单独按成本对出发和到达航班列表进行分类,然后使用heap逐个扩展下一个最佳组合,直到快得多 你有足够的。

这里是完整的算法 - 在Python中,但没有使用任何特殊的库,只是标准的数据结构,所以这应该可以很容易地转移到任何其他语言:

NUM_FLIGHTS, NUM_BEST = 1000, 100

# create test data: each entry corresponds to just the cost of one flight
from random import randint
dep = sorted([randint(1, 100) for i in range(NUM_FLIGHTS)])
arr = sorted([randint(1, 100) for i in range(NUM_FLIGHTS)])
def is_compatible(i, j): # for checking constraints, e.g. timing of flights
    return True          # but for now, assume no constraints

# get best combination using sorted lists and heap
from heapq import heappush, heappop
heap = [(dep[0] + arr[0], 0, 0)]   # initial: best combination from dep and arr
result = []                        # the result list
visited = set()                    # make sure not to add combinations twice
while heap and len(result) < NUM_BEST:
    cost, i, j = heappop(heap)     # get next-best combination
    if (i, j) in visited: continue # did we see those before? skip
    visited.add((i, j))
    if is_compatible(i, j):        # if 'compatible', add to results
        result.append((cost, dep[i], arr[j]))
    # add 'adjacent' combinations to the heap
    if i < len(dep) - 1:           # next-best departure + same arrival
        heappush(heap, (dep[i+1] + arr[j], i+1, j))
    if j < len(arr) - 1:           # same departure + next-best arrival
        heappush(heap, (dep[i] + arr[j+1], i, j+1))
print result

# just for testing: compare to brute-force (get best from all combinations)
comb = [(d, a) for d in dep for a in arr]
best = sorted((d+a, d, a) for (d, a) in comb)[:NUM_BEST]
print best
print result == best # True -> same results as brute force (just faster)

大致如下:

  • 按出发价格
  • 对出发航班dep和抵达航班arr进行排序
  • 创建一个堆并将最佳组合(最佳离开和最佳到达)以及列表中的相应索引放入堆中:(dep[0] + arr[0], 0, 0)
  • 重复,直到您有足够的组合或堆中没有更多元素:
    • 从堆中弹出最佳元素(按总费用排序)
    • 如果满足约束,则将其添加到结果集
    • 请确保您不使用visited设置
    • 将广告投放两次到结果集
    • 添加两个相邻的&#39;组合到堆中,即从dep和下一个arr获取相同的航班,从dep获取相同的航班,从arr获取相同的航班,即{{1} }和(dep[i+1] + arr[j], i+1, j)

这是一个非常小的工作示例。轴是(dep[i] + arr[j+1], i, j+1)dep个航班的(成本),表中的条目采用arr形式,其中n(c)m是条目的迭代次数添加到n(如果有的话),heap是费用,而c是它被添加到前10名&#39;的迭代次数。 m列表(如果有)。

result

请注意矩阵的列和行中的总和如何总是增加,因此最好的结果始终可以在左上角的某个三角形区域中找到。现在的想法是,如果你当前最好的组合(堆中的第一个组合)是dep\arr 1 3 4 6 7 2 0(3)1 1(5)4 4(6)8 8(8)- - 2 1(3)2 2(5)6 6(6)9 9(8)- - 3 2(4)3 3(6)7 7(7)- - - 4 3(5)5 5(7)- - - - 6 5(7)10 - - - - Result: (1,2), (1,2), (1,3), (3,2), (1,4), (3,2), (3,3), (2,4), (2,4), (1,6) ,那么检查没有用,例如,组合dep[i], arr[i]之前检查dep[i+2], arr[i],它必须具有较低的总成本,因此您将dep[i+1], arr[i](以及同样dep[i+1], arr[i])添加到堆中,并重复从堆中弹出下一个元素。


我将此算法的结果与您的蛮力方法的结果进行了比较,结果飞行是相同的,即算法有效,总是产生最佳结果。复杂性应该是 O(n log(n)),用于对出发和到达列表进行排序( n 是这些原始列表中的航班数量),加上 O (m log(m))用于堆循环( m 迭代, log(m)每次迭代工作, m 是结果列表中的元素数量。)

这可以找到 100,000 出发和 100,000 到达航班的最佳 1,000 组合(总计 1,000,000,000,000 不到一秒的可能组合

请注意,这些号码是针对您没有其他限制的情况,即每个出发航班可以与每个到达航班组合。如果存在约束,您可以使用上面代码中勾画的dep[i], arr[i+1]函数来检查这些并跳过该配对。这意味着,对于总成本较低的每个不兼容对,循环需要一次额外的迭代。这意味着在最坏情况中,例如,如果根本没有兼容的对,或者当唯一兼容的对是总成本最高的对时,算法可以事实上,扩展所有组合。

但平均而言,情况并非如此,算法的执行速度应该相当快。

答案 1 :(得分:1)

我认为最好的解决方案是使用一些SQL语句来做笛卡尔积。您可以根据数据本身,排序,范围选择等应用任何类型的过滤器。如下所示:

SELECT d.time as dep_time, a.time as arr_time, d.price+a.price as total_price
FROM departures d, arrivals a
WHERE a.time > d.time + X
ORDER BY d.price+a.price
LIMIT 0,100

实际上X甚至可以为0,但到达后应该会在出发后到达。

为什么我会选择SQL:

  • 它最接近数据本身,您不必查询它们
  • 高度优化,如果您使用索引,我确定您无法使用自己的代码超越其性能
  • 简单明了:)