生成具有所有排列的序列

时间:2010-02-12 16:17:58

标签: algorithm sequence

如何生成包含所有可能排列的最短序列?

实施例: 对于长度2,答案是121,因为该列表包含12和21,这些都是可能的排列。

对于长度3,答案是123121321,因为此列表包含所有可能的排列: 123,231,312,121(无效),213,132,321。

每个数字(在给定的排列中)可能只出现一次。

5 个答案:

答案 0 :(得分:6)

这种贪心算法产生相当短的最小序列。

  

更新:请注意for n ≥ 6, this algorithm does not produce the shortest possible string!

  • 收集所有排列。
  • 从集合中删除第一个排列。
  • a =第一个排列。
  • 在集合中查找与 a 结尾重叠最多的序列。如果存在平局,请按字典顺序选择顺序。从集合中删除所选序列,并将非重叠部分添加到 a 的末尾。重复此步骤,直到集合为空。

好奇的打破平局步骤对于正确性是必要的;随意打破领带似乎导致更长的字符串。

我验证了(通过编写更长,更慢的程序)这个算法给出的长度为4,123412314231243121342132413214321的答案确实是最短的答案。但是,对于长度为6,它会产生长度为873的答案,该答案比最短的已知解决方案更长。

算法为O( n 2 )。

Python中的实现:

import itertools

def costToAdd(a, b):
    for i in range(1, len(b)):
        if a.endswith(b[:-i]):
            return i
    return len(b)

def stringContainingAllPermutationsOf(s):
    perms = set(''.join(tpl) for tpl in itertools.permutations(s))
    perms.remove(s)
    a = s
    while perms:
        cost, next = min((costToAdd(a, x), x) for x in perms)
        perms.remove(next)
        a += next[-cost:]
    return a

此函数生成的字符串长度为1,3,9,33,153,873,5913,......似乎为this integer sequence

我有预感你可以做得比O更好( n 2 )。

答案 1 :(得分:5)

  • 创建所有排列。
  • 让每个人 置换表示一个节点 图形。
  • 现在,对于任何两个州添加一个 边缘值为1,如果他们共享 n-1个数字(来自的来源) 结束,并为目标 结束),两个如果他们共享n-2个数字 等等。
  • 现在,你只能找到 包含n的最短路径 顶点。

答案 2 :(得分:3)

这是一种快速算法,可生成包含所有排列的短字符串。我很确定它会产生最短的答案,但我手头没有完整的证据。

解释。以下是所有排列的树。图片不完整;想象这棵树永远向右走。

1 --+-- 12 --+-- 123 ...
    |        |
    |        +-- 231 ...
    |        |
    |        +-- 312 ...
    |
    +-- 21 --+-- 213 ...
             |
             +-- 132 ...
             |
             +-- 321 ...

此树级别 k 的节点都是长度的排列 ķ。此外,排列按特定顺序排列很多 每个排列与其上下邻居之间的重叠。

准确地说,只需添加下一个节点即可找到每个节点的第一个子节点 符号到底。例如,213的第一个孩子将是2134.其余的 通过旋转到第一个孩子以留下一个符号来找到孩子 一时间旋转2134将产生1342,3421,4213。

将所有节点放在给定的级别并将它们串在一起,重叠 尽可能地产生字符串1,121,123121321等

该序列中 n 字符串的长度为the sum for x=1 to n of x!。 (您可以通过观察相邻排列之间存在多少非重叠来证明这一点。兄弟姐妹在除1个符号之外的所有符号中重叠;除了2个符号外,所有兄弟姐妹都重叠;等等。)

证明草图。我还没有完全证明这是最好的解决方案,但这里是一个如何进行证明的草图。首先显示包含 n 不同排列的任何字符串的长度≥2 - 然后显示添加包含 n +1个不同排列的任何字符串长度为2 + 1.也就是说,再添加一个排列将花费您两位数。继续计算包含 n P r n 的字符串的最小长度 P r + 1个不同的排列,直到 n!。简而言之,这个序列是最佳的,因为你不能在某个地方使它变得更糟,以期在其他地方变得更好。它到处都是本地最佳的。所有的举动都是强制性的。

算法。鉴于所有这些背景,算法非常简单。将此树行走到所需的深度,并将该深度处的所有节点串在一起。

幸运的是,我们实际上不必在内存中构建树。

def build(node, s):
    """String together all descendants of the given node at the target depth."""
    d = len(node)  # depth of this node. depth of "213" is 3.
    n = len(s)     # target depth
    if d == n - 1:
        return node + s[n - 1] + node    # children of 213 join to make "2134213"
    else:
        c0 = node + s[d]                 # first child node
        children = [c0[i:] + c0[:i] for i in range(d + 1)]  # all child nodes
        strings = [build(c, s) for c in children]  # recurse to the desired depth
        for j in range(1, d + 1):
            strings[j] = strings[j][d:]  # cut off overlap with previous sibling
        return ''.join(strings)          # join what's left

def stringContainingAllPermutationsOf(s):
    return build(s[:1], s)

性能。上面的代码已经比我的其他解决方案快得多,并且它可以对大量字符串进行大量剪切和粘贴,您可以对其进行优化。该算法可以在时间和内存中运行,与输出的大小成比例。

答案 3 :(得分:0)

对于n 3个长度链为8 12312132 在我看来,我们正在使用循环系统-换句话说,它正在响。但是我们正在处理环,好像它是链一样。连锁确实是123121321 = 9 但是戒指是12312132 = 8 从序列 1 2312132 [1]的开头开始,我们取321的最后1个。

答案 4 :(得分:0)

这些被称为(最小长度)superpermutations (cf. Wikipedia)。 当匿名用户在4chan上发布新的下限时,对此的兴趣重新燃起。 (有关历史记录,请参见Wikipedia和许多其他网页。)

AFAIK,截止到今天,我们只知道:

  • 它们的长度为A180632(n)≤A007489(n)= Sum_ {k = 1..n} k!但是此边界仅在n≤5时才是尖锐的,即,对于n≤5我们具有相等,但对于n> 5则严格小于。
  • 下面提供了一个非常简单的递归算法,该算法产生长度A007489(n)的超置换,该置换总是回文的(但如上所述,这不是最小长度) n> 5)。
  • 对于n≥7,我们有更好的上限n! +(n-1)! +(n−2)! +(n−3)! + n-3。
  • 对于n≤5,所有最小SP都是已知的;对于所有n> 5,我们都不知道哪个是最小SP。
  • 对于n = 1、2、3、4,最小SP是唯一的(直到更改符号),由长度为A007489(1..4)的(1,121,123121321,123412314231243121342132413214321)给出(1, 3,9,33)。
  • 对于n = 5,有8个最小长度的不等式153 = A007489(5);下面的算法产生的回文是按字典顺序排列的第3个。
  • 对于n = 6,休斯顿生产了数千个最小的已知长度872 = A007489(6)-1,但AFAIK我们仍然不知道这是否最小。
  • 对于n = 7,Egan产生了一个长度为5906的长度(比上面给出的更好的上限少一个),但同样我们不知道这是否最小。

我编写了一个非常短的PARI / GP程序(您可以粘贴以运行它on the PARI/GP web site),该程序实现了标准算法,该算法产生长度为A007489(n)的回文超置换:

extend(S,n=vecmax(s))={ my(t); concat([
  if(#Set(s)<n, [], /* discard if not a permutation */
    s=concat([s, n+1, s]); /* Now merge with preceding segment: */ 
    forstep(i=min(#s, #t)-1, 0, -1,
      if(s[1..1+i]==t[#t-i..#t], s=s[2+i..-1]; break));
    t=s /* store as previous for next */
  )/*endif*/
  | s <- [ S[i+1..i+n] | i <- [0..#S-n] ]])
}
SSP=vector(6, n, s=if(n>1, extend(s), [1])); // gives the first 6, the 6th being non-minimal

我认为这很容易翻译成任何其他语言。 (对于非PARI语言用户:“ | x <-”表示“ for x in”。)