使用 Python 构建排列

Posted

技术标签:

【中文标题】使用 Python 构建排列【英文标题】:Building Permutation with Python 【发布时间】:2022-01-22 17:47:52 【问题描述】:

我正在尝试编写一个程序来使用递归获取一串字母的所有排列。由于我是 Python 初学者,因此我通过斐波那契数和阶乘等示例了解了递归。我了解这些数学示例,但我仍然在努力构建具有递归的函数式程序。我试图了解网络上的其他类似问题,但仍然无法掌握这个概念。

所以问题是:如何获得一串字母的所有排列?例如 str='abc' 。我的逻辑是这样的:

    为 3 个字母 abc 创建 3 个槽 添加一个字母,说“a”,它可以到达所有 3 个插槽 在前一个案例的基础上,输入“b”。现在只剩下 2 个插槽,b 可以进入 2 个插槽中的任何一个。 重复直到没有更多的插槽。现在我们达到了一个基本情况,没有。插槽数 = 0。

但我不能写代码

【问题讨论】:

这能回答你的问题吗? Finding all possible permutations of a given string in python 还有***.com/q/104420/4046632 你的算法很不错。为了编写 Python 代码,您需要决定的第一件事是:插槽使用什么数据结构?一个字符串?一个列表?字典?你来决定。 【参考方案1】:

我已经在 python 中实现了您的算法,使用包含点 '.' 作为插槽的字符串。当然,这意味着如果您在原始字符串中也有点,则该算法将无法工作。

我已经删除了我实现的大部分行;由你来完成它。在删除这些行之前,我对其进行了测试,它可以正常工作。

为 3 个字母 abc 创建 3 个插槽:'.' * len(s) 添加一个字母,说'a',它可以到达所有3个插槽:在for循环中使用函数insert,其中islots中每个'.'的位置 在前一个案例的基础上,输入“b”。现在只剩下 2 个 slot,b 可以到 2 个 slot 中的任何一个:在每个 insert 之后,进行递归调用 重复直到没有更多的插槽。现在我们达到了一个基本情况,没有。插槽数 = 0
def permutations(s):
    return helper_perms(s, '.' * len(s), len(s))

def helper_perms(s, slots, k):
    if ??? # base case
        return ???
    else:
        results = []
        for i,c in enumerate(slots):
            if c == '.':
                results.extend(helper_perms(s, ???, ???)) # insert some char from s into slots and make a recursive call
        return results

def insert(slots, i, c):
    return ??? # return a new string with character c inserted at position i in slots

print( permutations('abc') )
# ['cba', 'cab', 'bca', 'acb', 'bac', 'abc']

【讨论】:

【参考方案2】:

我们可以使用inductive reasoning 编写任何可迭代的tpermutations(t) -

    如果t 为空,则产生空排列,() (感应)t 至少有一个元素。对于递归子问题permutations(t[1:])的所有p,对于inserts(p, t[0])的所有i,产生i
def permutations(t):
  if not t:
    yield ()                       # 1. empty t
  else:
    for p in permutations(t[1:]):  # 2. at least one element
      for i in inserts(p, t[0]):
        yield i

inserts(t, x) 也可以使用归纳推理来编写 -

    如果输入 t 为空,则产生最终插入,(x) (感应)t 至少有一个元素。 yield x 前置到t 和所有i 的递归子问题inserts(t[1:], x) yield t[0] 前置到i
def inserts(t, x):
  if not t:
    yield (x)                        # 1. empty t
  else:
    yield (x, *t)                    # 2. at least one element
    for i in inserts(t[1:], x):
      yield (t[0], *i)
t = ("?","?","?","?")
for p in permutations(t):
  print("".join(p))
????
????
????
????
????
????
????
????
????
????
????
????
????
????
????
????
????
????
????
????
????
????
????
????

Python 对生成器有强大的支持,并使用yield from .. 提供委托。我们可以简化上述定义,同时保持完全相同的行为 -

def permutations(t):
  if not t:
    yield ()
  else:
    for p in permutations(t[1:]):
      yield from inserts(p, t[0])  # <-
def inserts(t, x):
  if not t:
    yield (x)
  else:
    yield (x, *t)
    yield from map(lambda r: (t[0], *r), inserts(t[1:], x)) # <-

生成器非常适合组合数学,因为使用它们是自然而直接的 -

# find all permutations where red is left of green
for p in permutations(t):
  if p.index("?") < p.index("?"):
    print("".join(p))
????
????
????
????
????
????
????
????
????
????
????
????
# find all permutations where blue and yellow are adjacent
for p in permutations(t):
  if abs(p.index("?") - p.index("?")) == 1:
    print("".join(p))
????
????
????
????
????
????
????
????
????
????
????
????

此外,生成器可以随时暂停/停止,允许我们跳过潜在的数百万次计算以解决非常大的问题 -

# which permutation is in rainbow order?
for (n, p) in enumerate(permutations(t)):
  if p == ("?", "?", "?", "?"):
    print(f"permutation n is in rainbow order, p")
    break  # <- stops generating additional permutations
permutation 16 is in rainbow order, ('?', '?', '?', '?')

【讨论】:

以上是关于使用 Python 构建排列的主要内容,如果未能解决你的问题,请参考以下文章

leetcode-46. 全排列--回溯算法--python

算法学习1920. 基于排列构建数组(java / c / c++ / python / go / rust)

算法学习1920. 基于排列构建数组(java / c / c++ / python / go / rust)

LeetCode刷题1920-简单-给予排列构建数组

Python 排列组合

使用特定规则在 Python 中生成排列