我已经查看了关于排列的其他几个问题,这种变体似乎没有出现。我正在寻找一种简单的方法来产生有偏见的排列。这就是我的意思。
假设我有两个列表(虽然我需要解决 N 列表):
l1 = [a, b, c]
l2 = [d, e, f]
这些列表的排列看起来像[(a,d),(a,e),(a,f),(b,d),(b,e),(b,f)...] 。然而,在我的世界中,对排列的元素进行评分并总结以重视排列。假设 a , d 和 e 值2分, b , c , f 值1分。那么一些样本排列的值是:
(a,d) = 4
(a,e) = 4
(a,f) = 3
(c,f) = 2
我需要生成所有排列,但我想在较低的值排列之前生成高值排列。
假设每个列表的元素按降序排序,是否有一种很好的方法可以按值顺序生成排列?
(显然我可以生成所有的排列并对它们进行排序,但我更愿意编写一个生成器,因为排列的数量可能很大。)
答案 0 :(得分:1)
使用简单的贪婪式算法,这应该很容易接近。也就是说,我假设您可以访问特定值,而不仅仅是值的排序列表。还假设它已经排序。
l1 = [(a, 2), (b, 1), (c, 1)]
l2 = [(d, 2), (e, 2), (f, 1)]
事实是,这是非常重要的,但是这就是你如何处理这个问题(代码可能会在以后发生,因为正如我所说,它实际上是非平凡的。)
假设您在任何给定点上有三种可能的行动:
然后你必须简单地跟踪这三者中每一个的价值,并在每个时间步长选择最佳值。它并不完全是懒惰的,因为你必须在每个时间步都更新这三个值,它非常接近。
要实际实现这一点,我们必须保留一个数据结构:
next_entries: {*l1: last entry of l2 explored}
next_values: {*l1: l1 entry + next l2 entry}
此时可以完成上述三个可能点的计算。再次,可以生成代码,可能会做,但它可能以可读的方式执行大约20条密集线。
答案 1 :(得分:1)
例如,让l1 = {6,4,3,1}和l2 = {5,4,1}。在2D平面上将它们绘制为水平和垂直线。
然后兴趣点是所有的交叉点。我们应该报告这些交叉点,以便从(inf,inf)移动到(0,0)的假想扫描线接触它们。请注意,位于水平线上的点不能比同一直线上的另一个点更早报告。因此,对于每条水平线,我们必须只检查最右边的点。从所有这些点,我们必须选择具有最大坐标总和的点。这可以通过堆数据结构来完成。
最初,我们将位于最右边垂直线上的所有点放入堆中。然后我们从堆中提取顶点,产生它,最后将其左邻居放入堆中(如果有的话)。因此堆总是包含最多len(l1)个元素,并且每个新生成的点都花费我们O(log(len(l1)))。如果我们选择l1作为两个给定的最小列表,则可以改进解决方案。
以下是示例解决方案:
import heapq
a = [("a", 6), ("b", 4), ("c", 3), ("d", 1)]
b = [("e", 5), ("f", 5), ("g", 4), ("h", 2)]
class Pair:
def __init__(self, i, j, value):
self.i = i
self.j = j
self.value = value
def __cmp__(self, other):
return other.value - self.value
def solution(a, b):
heap = []
for i in range(len(a)):
heapq.heappush(heap, Pair(i, 0, a[i][1] + b[0][1]))
while len(heap) > 0:
pair = heapq.heappop(heap)
yield (a[pair.i], b[pair.j], pair.value)
if pair.j + 1 < len(b):
heapq.heappush(heap, Pair(pair.i, pair.j + 1, a[pair.i][1] + b[pair.j + 1][1]))
for (a, b, value) in solution(a, b):
print ("%s %s -> %d" % (a, b, value))
当我们进入更高维度(超过2个列表要合并)时,情况会变得更糟。它可以在2D解决方案的基础上通过memoization来解决,因此我们首先为l1,l2构建一个类似惰性列表的数据结构的答案,然后再次应用相同的算法,对于这个memoized列表和l3作为参数,等等。必须采取的最后一步 - 我们应该始终使用长度较小的数组作为l1,或者在开头将所有l1元素推入堆中。
N列表here的完整代码示例,因为它太长了。