桶的置换算法

时间:2017-11-19 11:47:30

标签: algorithm permutation

我正在寻找一种像这样工作的算法

permutateBuckets([A,B,C])

并给出以下结果:

[ [[A,B,C]],
  [[A,B],[C]], [[A,C],[B]], [[B,C],[A]], [[A],[B,C]], [[B],[A,C]], [[C],[A,B]],
  [[A],[B],[C]], [[A],[C],[B]], [[B],[A],[C]], [[B],[C],[A]], [[C],[A],[B]], [[C],[B],[A]]
]

一般来说:

[1,2,...,n]的排列应该包括1到n个包含输入值的桶的任何可能排列,桶内值的顺序是不相关的(例如[1,2]等于[2,1]),只有包含桶的顺序很重要(例如[[1,2],[3]]不同于[[3],[1,2]])。

每个输入元素必须恰好在一个桶中才能使结果有效(例如[1,2]的输入不能给[[1]](缺少2),或[[1,2],[ 1]](1出现两次)作为输出。)

1 个答案:

答案 0 :(得分:1)

最简单的方法是递归的:

 Make [[A]] list
 Insert new item in all possible places - 
    before current sublists
    between all sublists
    after current sublists
    into every sublist

例如,list [[B][A]]生成5个带有项目C的新列表 - 要插入C的位置是:

  [   [B]   [A]   ]
    ^  ^  ^  ^  ^

和三个二级列表[[A],[B]], [[B],[A]], [[A,B]]生成5 + 5 + 3 = 13个级别3列表。

替代方式:
生成从1 ... 1到1..n的所有n长度非递减序列,并为每个序列生成唯一的排列。 这些排列的值对应于每个项目的桶号。例如,122序列给出3个对应于分布的排列:

1 2 2     [1],[2, 3] 
2 1 2     [2],[1, 3] 
2 2 1     [3],[1, 2]

在任何情况下,分布数量都会迅速上升(有序的贝尔数1, 3, 13, 75, 541, 4683, 47293, 545835, 7087261, 102247563...

在Delphi中实现迭代方法(here处的完全FP兼容代码)

 procedure GenDistributions(N: Integer);
  var
    seq, t, i, mx: Integer;
    Data: array of Byte;
    Dist: TBytes2D;
  begin
    SetLength(Data, N);

    //there are n-1 places for incrementing
    //so 2^(n-1) possible sequences
    for seq := 0 to 1 shl (N - 1) - 1 do begin
      t := seq;
      mx := 0;
      Data[0] := mx;
      for i := 1 to N - 1 do begin
        mx := mx + (t and 1); //check for the lowest bit
        Data[i] := mx;
        t := t shr 1;
      end;

      //here Data contains nondecreasing sequence 0..mx, increment is 0 or 1
      //Data[i] corresponds to the number of sublist which item i belongs to

      repeat
        Dist := nil;
        SetLength(Dist, mx + 1); // reset result array into [][][] state

        for i := 0 to N - 1 do
          Dist[Data[i]] := Dist[Data[i]] + [i]; //add item to calculated sublist

        PrintOut(Dist);
      until not NextPerm(Data);  //generates next permutation if possible

    end;

现在Python递归实现(ideone

import copy
cnt = 0

def ModifySublist(Ls, idx, value):
    res = copy.deepcopy(Ls)
    res[idx].append(value)
    return res

def InsertSublist(Ls, idx, value):
    res = copy.deepcopy(Ls)
    res.insert(idx, [value])
    return res

def GenDists(AList, Level, Limit):
    global cnt
    if (Level==Limit):
        print( AList)
        cnt += 1
    else:
        for i in range(len(AList)):
            GenDists(ModifySublist(AList, i, Level), Level + 1, Limit)
            GenDists(InsertSublist(AList, i, Level), Level + 1, Limit)
        GenDists(InsertSublist(AList, len(AList), Level), Level + 1, Limit)

GenDists([], 0, 3)
print(cnt)

编辑:@mhmnn使用自定义项目在ideone克隆此代码。