如何通过减少周期来产生排列?

时间:2019-07-30 23:53:10

标签: python-3.x algorithm permutation

这里有两个相关的SO问题enter image description here 1帮助我制定了初步解决方案。

想要这样做的原因是通过编辑距离将置换输入到Damerau-Levenshtein NFA中;排列的数量快速增长,因此最好延迟N个(C)循环N个排列候选,直到NFA(N-C)次迭代。

我只研究了工程数学,直至微分方程和离散数学,因此我缺乏从正式角度进行这项任务的基础。如果有人可以提供参考材料来帮助我正确地理解此问题,我将不胜感激!

通过简要的经验分析,我注意到可以使用以下过程为所有C周期N个置换生成交换:

  1. 生成N个元素(梳子)的所有2个组合
  2. 细分为两个组合的最小元素相同的数组(ncombs)
  3. 生成ncomb(pcombs)(N-C)组合的笛卡尔积
  4. 总pcombs以获得将生成所有C周期N个置换(掉期)的掉期列表

代码为2

我的Python有点生锈,因此对代码的建议很有帮助(我感觉应该将第17、20和21行结合起来。我不确定是否应该列出结果的清单)我不知道为什么第10行不能是ncombs + = ...而不是ncombs.append(...))。

我的主要问题是如何正确解决此问题。我通过寻找解决方案来进行自己的尽职调查,但是我确信有更好的方法。我还只验证了针对N = 3和N = 4的解决方案,真的正确吗?

理想的解决方案在功能上与堆算法相同,除了它会以递减的循环顺序生成排列(通过最小交换次数来生成排列,然后增加)。

1 个答案:

答案 0 :(得分:1)

这与Heap的效率相差甚远,但是在排列中,它仅产生受所需的循环数k限制的必要循环组合。我们使用k的分区为每个分区创建周期的所有组合。枚举实际排列只是将每个周期n-1应用一次的笛卡尔积,其中n是周期长度。

递归Python 3代码:

from math import ceil

def partitions(N, K, high=float('inf')):
  if K == 1:
    return [[N]]
  result = []
  low = ceil(N / K)
  high = min(high, N-K+1)
  for k in range(high, low - 1, -1):
    for sfx in partitions(N-k, K - 1, k):
      result.append([k] + sfx)
  return result

print("partitions(10, 3):\n%s\n" % partitions(10, 3))

def combs(ns, subs):
  def g(i, _subs):
    if i == len(ns):
      return [tuple(tuple(x) for x in _subs)]

    res = []
    cardinalities = set()

    def h(j):
      temp = [x[:] for x in _subs]
      temp[j].append(ns[i])
      res.extend(g(i + 1, temp))

    for j in range(len(subs)):
      if not _subs[j] and not subs[j] in cardinalities:
        h(j)
        cardinalities.add(subs[j])

      elif _subs[j] and len(_subs[j]) < subs[j]:
        h(j)

    return res

  _subs = [[] for x in subs]

  return g(0, _subs)

A = [1,2,3,4]
ns = [2, 2]
print("combs(%s, %s):\n%s\n" % (A, ns, combs(A, ns)))
A = [0,1,2,3,4,5,6,7,8,9,10,11]
ns = [3, 3, 3, 3]
print("num combs(%s, %s):\n%s\n" % (A, ns, len(combs(A, ns))))

def apply_cycle(A, cycle):
  n = len(cycle)
  last = A[ cycle[n-1] ]
  for i in range(n-1, 0, -1):
    A[ cycle[i] ] = A[ cycle[i-1] ]
  A[ cycle[0] ] = last

def permutations_by_cycle_count(n, num_cycles):
  arr = [x for x in range(n)]

  cycle_combs = []
  for partition in partitions(n, num_cycles):
    cycle_combs.extend(combs(arr, partition))

  result = {}

  def f(A, cycle_comb, i):
    if i == len(cycle_comb):
      result[cycle_comb].append(A)
      return

    if len(cycle_comb[i]) == 1:
      f(A[:], cycle_comb, i+1)

    for k in range(1, len(cycle_comb[i])):
      apply_cycle(A, cycle_comb[i])
      f(A[:], cycle_comb, i+1)

    apply_cycle(A, cycle_comb[i])

  for cycle_comb in cycle_combs:
    result[cycle_comb] = []
    f(arr, cycle_comb, 0)

  return result

result = permutations_by_cycle_count(4, 2)

print("permutations_by_cycle_count(4, 2):\n")

for e in result:
  print("%s: %s\n" % (e, result[e]))

输出:

partitions(10, 3):
[[8, 1, 1], [7, 2, 1], [6, 3, 1], [6, 2, 2], [5, 4, 1], [5, 3, 2], [4, 4, 2], [4, 3, 3]]

# These are the cycle combinations
combs([1, 2, 3, 4], [2, 2]):
[((1, 2), (3, 4)), ((1, 3), (2, 4)), ((1, 4), (2, 3))]

num combs([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [3, 3, 3, 3]):
15400

permutations_by_cycle_count(4, 2):

((0, 1, 2), (3,)): [[2, 0, 1, 3], [1, 2, 0, 3]]

((0, 1, 3), (2,)): [[3, 0, 2, 1], [1, 3, 2, 0]]

((0, 2, 3), (1,)): [[3, 1, 0, 2], [2, 1, 3, 0]]

((1, 2, 3), (0,)): [[0, 3, 1, 2], [0, 2, 3, 1]]

((0, 1), (2, 3)): [[1, 0, 3, 2]]

((0, 2), (1, 3)): [[2, 3, 0, 1]]

((0, 3), (1, 2)): [[3, 2, 1, 0]]