使用 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
,其中i
是slots
中每个'.'
的位置
在前一个案例的基础上,输入“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 编写任何可迭代的t
的permutations(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)