从列表中形成随机对(有点......)

时间:2011-08-29 03:54:34

标签: list random python

跳至上一次修改

我有一个Person个对象的列表,我需要将它们与randomize_pairs函数随机配对,每个Person对象都有一个属性target配对。

我的限制是没有人可以与自己配对(duh),他们不应该与同一个人配对两次。

我通过制作一个临时列表来解决这个问题,如果满足约束条件就会让人们离开,但我确信有更清洁/更好/更pythonic的方法。有人知道吗?


修改

我在这个问题中经常使用“对”这个词,但那是错误的词。这是针对一个游戏,每个人被指定为另一个人作为目标,所以这些是单向关系,你的目标不一定是你的目标。

目标只会在每轮开始时更改,所以一次性完成。


编辑2

以下是我现在已经解决的问题虽然可以改进,但我将问题保持开放。

def randomize_targets(players):
    # get player count
    count = len(players)
    # copy the list of players
    available_targets = list(players)
    # shuffle the player order so if the last one has to have the same
    # target twice it's not always the same player
    players = list(players)
    random.shuffle(players)
    # loop over each player
    for player in players:
        # get the list of possible targets
        potential_targets = [target for target in available_targets \
                             if target != player \
                             and target != player.target]
        # try to pick one at random
        try:
            target = random.choice(potential_targets)
        # if we have to, use the same target as last time
        except IndexError:
            pass
        # remove the target from the available targets list
        available_targets.remove(target)
        # assign target
        player.target = target

编辑3

我决定采用这种方法,即使我不喜欢潜在的长时间循环,直到找到一个至少起作用的组合,它总能产生有效的结果

def randomize_targets2 (players):
    targets = list(players)
    # run this until it generates a valid result
    valid = False
    while not valid:
        # randomize the targets
        random.shuffle(targets)
        # validate them
        round_valid = True
        for player, target in zip(players, targets):
            round_valid = round_valid and player != target and player.target != target
        valid = round_valid

    # apply the validated targets
    for player, target in zip(players, targets):
        player.target = target

2 个答案:

答案 0 :(得分:6)

我认为,由于您想随机选择人,因此您选择的列表可以提供快速随机访问。一个简单的解决方案就是对整个列表进行洗牌,然后从列表的前面成对配对。

Fisher-Yates shuffle是一种随机播放列表的快捷方式。

然后你可以直接将它们配对:

for x from 0 to persons.size(), x += 2
    pair(persons.get(i), persons.get(i + 1));

由于元素是独一无二的,因此您不必担心人们会两次配对或与自己配对。

另外要小心确保您的列表首先包含偶数人数!如果总数是奇数,你将不得不以某种方式处理列表末尾的额外人员。

答案 1 :(得分:1)

您可以使用列表获取所有可用人员 您还可以将每个人的dict映射到他们已经配对的所有人的一组(并且还包括他们自己组中的每个人)。
然后对于每个人,你可以用他们已经配对的一组人来减去所有人的集合,并对该集合中的任何项目进行采样(使用random.sample)(设置减法可能非常有效)。
您还需要确保如果您将人A与人B配对,那么当B轮到来时,您会注意到现有的配对并且只是跳过该迭代。
当然,不要忘记用新的配对更新你的字典,这样你就不会再次制作相同的配对。
这种方法对我来说似乎很优雅,因为你可以很容易地添加任意忽略规则(即A人永远不想与B人配对),只需在相关集中添加它们即可。