列举排列

时间:2017-10-13 13:50:56

标签: algorithm

对于不同排列的数量,许多组合问题具有n!/(m!* p!* q!...)的形式。是否有任何有效的算法来枚举所有不同的排列?

让我们用一个例子来解决问题。假设有10个人想玩5v5游戏。为了帮助他们建立一场比赛(即谁进入每支球队),我们想列举所有可能的对决。有多少场比赛?总的来说,有10个! = 3628800安排球员的可能方式。然而,交换球队1内的球员并没有改变任何东西(A + B + C + D + E对F + G + H + I + J与B + A + C + D + E对F + G + H相同+ I + J),所以我们必须将这个数字除以5!交换球队内的球员相同2.将球队1和球队2交换到同一场比赛,所以我们想把最终结果除以2.最后,有10!/(5!* 5!* 2!) = 126个不同的对决。有没有办法找到它们?

天真算法将枚举所有玩家排列,并且仅返回对战的“规范”表示(例如,在团队内部玩家必须按字典顺序排序)。然而,这非常低效;对于8对8的游戏,我们必须枚举并评估16! (超过20万亿)排列,以列举6435个不同的对决。是否有任何非天真的选择?

2 个答案:

答案 0 :(得分:0)

“A”必须在其中一个团队中,所以我们可以创建所有内部都有“A”的团队,以避免获得相同的组合。

对于每个组,我们都会查看可以添加的所有可能的新元素。如果你添加一个元素,那么只允许添加稍后在该列表中的元素(在still_available = available [i:]中完成),以便不为每个团队获得每个排列。

如果一个组有足够的成员(5)将其添加到列表中,并且有可能。

由于你需要至少5个成员,你需要添加索引0,...,len(可用) - (5 - len(fixed_group) - 1)中的一个成员,因为否则只会有len(fixed_group) )你已经选择并且(5 - len(fixed_group) - 1)可用左边小于5.

input = ["B", "C", "D", "E", "F", "G", "H", "I", "J"]
global_poss_groups = []

def recursive_find_groups(fixed_group, available):
    if len(fixed_group) == 5:
        global_poss_groups.append(fixed_group)
        return -1
    for i in range(len(available) - ( 5 - len(fixed_group) - 1)):
        still_available = available[i:]
        new_element = still_available.pop(0)
        new_group = fixed_group[:]
        new_group.append(new_element)
        recursive_find_groups(new_group, still_available)

recursive_find_groups(["A"], input)
print(global_poss_groups)
print(len(global_poss_groups))

答案 1 :(得分:0)

枚举而不重复的关键通常是选择规范排序。例如,要枚举从大小为k的Universe中选择的n元素的组合,我们可以通过仅按排序顺序生成组合来确保唯一性。 (即使宇宙中包含重复的元素,也可以使其工作,但这与此无关。)

在将球员分配给球队的情况下,我们正在寻找一个大小为kn的球体划分为k个球队的n球员。 (在您的示例中,k是两个,但很容易推广到k的任何值。)我们通过以下方式进行规范选择:

  • 对每个队伍中的球员进行排序

  • 按第一位玩家排序球队

我们在参数k上递归枚举分区。

如果k为1,则解决方案只是一组玩家(按排序顺序)作为一个团队。对于k的任何较大值,我们将第一个玩家放入第一个团队,然后:

  • 对于使用所选玩家创建的每个第一个团队以及剩余玩家中n-1个玩家的一些(已排序)组合:

    • 以递归方式将其余(k-1)*n名玩家的所有分区枚举为k-1个小组。

解决了不同(但相似)的分区枚举问题in this SO post。该答案包含一般迭代机制,可用于许多类似的问题,包括这个问题。