为一组组合建立最高分

时间:2011-12-26 21:20:16

标签: python algorithm

我在python中编程。

我有以下表格的数据:

(A, B, C, D, E, F, G, H, I)

此数据的细分与分数相关联,例如:

scores:

    (A, B, C, D) = .99
    (A, B, C, E) = .77
    (A, B, E) = .66
    (G,) = 1
    (I,) = .03
    (H, I) = .55
    (I, H) = .15
    (E, F, G) = .79
    (B,) = .93
    (A, C) = .46
    (D,) = .23
    (D, F, G) = .6
    (F, G, H) = .34
    (H,) = .09
    (Y, Z) = 1

我们可以按如下方式获得这些数据的分数:

A B C E + D F G + H I = .77 * .6 * .55 = 0.2541

另一种可能性是:

A B C D + E F G + H + I = .99 * .79 * .09 * .03 = 0.00211167

所以,第一个组合得分更高。

我希望编写一个算法来建立高于最高分数的数据。数据成员不应重复多次。换句话说:

A B C E + E F G + D + H I 

无效。你会怎么建议我去解决这个问题?

谢谢,

百里

修改 我应该澄清(H,I)!=(I,H)和(I,H)不是ABCDEFGHI的子段,而是ABIHJ的子段。 我应该提到的另一件事是得分是一个非常大的集合(数百万),我们计算得分的段的平均长度大约为10.此外,我计算得分的方式可能会在未来发生变化。也许我想添加子段并取平均值而不是乘法,谁知道...因为这个原因可能更好地分离代码,从而根据分数的实际计算来计算可能的组合。目前,我倾向于认为itertools.combinations可能提供一个很好的起点。

4 个答案:

答案 0 :(得分:2)

这听起来像伪装的NP完全问题,是Knapsack problem的衍生物。这意味着您可能需要完成所有可能性以获得精确的解决方案。

即使......等等。您的值介于0和1之间。这样结果只会在最小值保持不变时变小。因此,解决方案是微不足道的:获得具有最高价值的单个组,并完成。 (我知道这可能不是你想要的,但你可能不得不添加另一个条件,例如必须使用所有元素......?)

蛮力方法的开始:

import operator

segment_scores = {(A, B, C, D): .99, (A, B, C, E): .77} #...

def isvalid(segments):
    """returns True if there are no duplicates
    for i in range(len(segments)-1):
        for element in segments[i]:
            for j in range(len(segments)-i-1):
              othersegment = segments[j+i+1]
              if element in othersegment:
                return False
    return True

    better way:
    """
    flattened = [item for sublist in segments for item in sublist]
    # http://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python
    return len(set(flattened)) == len(flattened)

def getscore(segments):
    """
    p = 1.0
    for segment in segments:
      p *= segment_scores[segment]
    return p

    better way:
    """
    return reduce(operator.mul, [segment_scores[segment] for segment in segments])

现在,创建所有2 ^(num段)可能的段组合,检查每个段是否有效,如果是,则在保持当前赢家及其高分的同时计算得分。只是一个起点...

确定只是另一个更新:这里有很多可供优化的空间,特别是因为你正在成倍增加(我现在假设你必须使用每个元素)。

  • 由于您的总得分从未增加,您可以放弃任何低于当前高分的探索路径[segment0,segment1],因为您只能获得任何segment2的工作。

  • 如果您不是只迭代所有可能性,而是首先探索包含第一个段的所有段列表(通过递归浏览包含第二个段的所有段列表等等),您可以打破例如,第一段和第二段无效,即无需探索分组(A,B,C,D)和(A,B,C,D,E)的所有可能性

  • 由于倍增伤害,尝试最小化段数可能是一个合适的启发式方法,所以从高分数的大段开始。

答案 1 :(得分:2)

通过使用递归(对于每个片段按顺序,我们使用片段递归地找到最佳分数,以及不使用片段的最佳分数。如果没有可能的片段组合,则分配0分数剩下的项目):

segment_scores = (('A', 'B', 'C', 'D'), .99), (('A', 'B', 'C', 'E'), .77) #, ...

def best_score_for(items, segments, subtotal = 1.0):
    if not items: return subtotal
    if not segments: return 0.0
    segment, score = segments[0]
    best_without = best_score_for(items, segments[1:], subtotal)
    return max(
        best_score_for(items.difference(segment), segments[1:], subtotal * score),
        best_without
    ) if items.issuperset(segment) else best_without

best_score_for(set('ABCDEFGHI'), segment_scores) # .430155

答案 2 :(得分:1)

首先,我建议为有意义的细分分配一个唯一的符号。

然后你可能想要那些符号的组合(或者可能是排列,我确定你比我更了解你的问题),以及你用来抛弃不良可能性的“legal_segment_combination”函数 - 基于矩阵哪些冲突,哪些冲突。

>>> import itertools
>>> itertools.combinations([1,2,3,4], 2)
<itertools.combinations object at 0x7fbac9c709f0>
>>> list(itertools.combinations([1,2,3,4], 2))
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
>>>

然后最大化使其超过legal_segment_combination()的有效可能性。

答案 3 :(得分:0)

首先,您可以取每个分数的对数,从那时起问题是最大化分数的总和而不是产品。然后,您可以将问题解决为Assignment Problem,您为每个数据点分配一个序列。