联盟搜索算法

Posted

技术标签:

【中文标题】联盟搜索算法【英文标题】:Coalition Search Algorithm 【发布时间】:2011-11-05 08:59:53 【问题描述】:

我正在寻找一种在 C、C++、Python 或 Java 中实现的算法,该算法可以计算 n 个代理的获胜联盟集合,其中每个代理具有不同的票数。我会很感激任何提示。谢谢!

【问题讨论】:

如果你会在其他地方找到,请在这里发布.. 【参考方案1】:

换句话说,你有一个数组X[1..n],并且想要拥有它的所有子集sum(subset) >= 1/2 * sum(X),对吧?

这可能意味着整个系列都符合条件。

之后,您可以删除任何具有X[k] < 1/2 * sum(X) 的元素k,并且每个这样的联盟都可以作为答案。

之后,您可以继续逐个删除元素,当您达到总和的一半时停止。

显然不是最有效的解决方案:如果你已经尝试过k1=2,k2=1,你不想放弃k1=1,k2=2——但我相信你可以处理这个问题。

【讨论】:

【参考方案2】:

最好通过递归地分成两种情况来解决这个问题:找到所有获胜的“联盟”,包括最后一个“代理人”和所有没有最后一个“代理人”的人。现在,对于这些子问题中的每一个,都可以应用相同的逻辑,在包含最后一个“代理”的情况下,目标投票数较低。当目标票数小于或等于零,或者没有更多的代理时,停止递归。

请注意,在这样的算法中,根据投票数对代理进行排序是有益的。

示例实现:

from itertools import combinations

def _winning_coalitions(agents, target_votes):
    """recursive solving function

    @param agents: sequence of (name, votes) pairs
    @param target_votes: minimum number of votes for a coalition
    """
    if target_votes <= 0:
        # stop the recursion
        for coalition_size in range(len(agents)+1):
            for coalition in combinations(agents, coalition_size):
                yield coalition
    elif not agents:
        pass # no agents, so no possible coalitions
    else:
        agent_name, agent_votes = agents[-1]
        agents = agents[:-1]
        for coalition in _winning_coalitions(agents, target_votes-agent_votes):
            yield ((agent_name, agent_votes),) + coalition
            if sum([votes for (name, votes) in coalition]) >= target_votes:
                yield coalition

def winning_coalitions(agents):
    """find all coalitions with at least target_votes combined votes

    @param agents: dictionary of the form: name -> number of votes
    """
    target_votes = (sum(agents.values())-1)//2+1
    agents = sorted(agents.items(), key=operator.itemgetter(1))
    coalitions = _winning_coalitions(agents, target_votes)
    return sorted([sorted([name for (name, votes) in c]) for c in coalitions])

在 Python 解释器中:

>>> agents = "Alice": 3, "Bob": 5, "Charlie": 7, "Dave": 4
>>> # divide sum of votes by 2, rounding up
>>> target_votes = (sum(agents.values())-1)//2+1
>>> # solve!
>>> coalitions = winning_coalitions(agents, target_votes)
>>> sorted([sorted(c) for c in coalitions])
[['Alice', 'Bob', 'Charlie'],
 ['Alice', 'Bob', 'Charlie', 'Dave'],
 ['Alice', 'Bob', 'Dave'],
 ['Alice', 'Charlie'],
 ['Alice', 'Charlie', 'Dave'],
 ['Bob', 'Charlie'],
 ['Bob', 'Charlie', 'Dave'],
 ['Charlie', 'Dave']]

【讨论】:

【参考方案3】:

将每个agent的投票数排列成一个数组,并从右开始计算部分和,这样只需查找部分和就可以找到SUM_i = k to n Votes[i]。

然后对 1, 2, ...n 的所有可能子集进行回溯搜索。在回溯的任何时候,您已经接受了代理 0..i - 1 的某个子集,并且您从部分总和中知道其他代理可用的最大可能票数。因此,您可以查看当前子集是否可以通过代理编号 >= i 进行扩展以形成获胜联盟,如果不能,则将其丢弃。

这为您提供了一个回溯搜索,您只考虑一个子集,如果它已经是一个获胜的联盟,或者您将扩展它成为一个获胜的联盟。所以我认为回溯搜索的成本是你发现的获胜联盟规模的总和,这似乎接近最优。我很想在运行此之前重新安排代理,以便您首先处理得票最多的代理,但目前我没有看到任何论点说您从中获得了很多。

实际上-从 Alf 的回答中得到一个提示-如果您从全套代理开始,然后使用回溯搜索来决定丢弃哪些代理,生活会容易得多。那么你就不需要一个部分和数组,而且你只生成你想要的子集。是的,无需提前订购代理。

【讨论】:

OP 想要找到所有可能的“联盟”,因此顺序无关紧要(重新排列不会带来任何好处)。

以上是关于联盟搜索算法的主要内容,如果未能解决你的问题,请参考以下文章

五月集训(第15天) —— 深度优先搜索

五月集训(第17天) —— 广度优先搜索

七月集训(第17天) —— 广度优先搜索

七月集训(第15天) —— 深度优先搜索

六月集训(第15天) —— 深度优先搜索

七月集训(第17天) —— 广度优先搜索