联盟搜索算法
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 想要找到所有可能的“联盟”,因此顺序无关紧要(重新排列不会带来任何好处)。以上是关于联盟搜索算法的主要内容,如果未能解决你的问题,请参考以下文章