联盟搜索算法

Coalition Search Algorithm

本文关键字:搜索算法 联盟      更新时间:2023-10-16

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

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

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

之后,你可以去掉任何一个有X[k] < 1/2 * sum(X)的元素k,这样的一个联盟也可以作为一个答案。

之后,可以一个接一个地删除元素,直到删除到和的一半时停止。

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

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

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

implmentation例子:

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']]

将每个代理的投票数排列成一个数组,并从右开始计算部分和,这样您就可以通过查找部分和来找出SUM_i = k to n votes [i]。

然后对{1,2,…n}的所有可能子集进行回溯搜索。在回溯的任何一点上,您都接受了代理0..I - 1,从部分和可以知道其他代理的最大可能票数。因此,您可以查看当前子集是否可以扩展为代理数>= i以形成获胜的联盟,如果不能,则丢弃它。

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

实际上——从Alf的回答中得到一个提示——如果你从完整的代理集合开始,然后使用回溯搜索来决定放弃哪些代理,生活会容易得多。这样你就不需要一个部分和的数组,你只需要生成你想要的子集。是的,没有必要提前订购代理商。