我正在尝试实施Steinhaus-Johnson-Trotter algorithm来生成排列。我的代码如下:
permutations :: [a] -> [[a]]
permutations [] = []
permutations (x:[]) = [[x]]
permutations xs = [ys ++ [xs !! i] | i <- [len,len-1..0], ys <- permutations (delete i xs)]
where len = (length xs)
delete i xs = take i xs ++ drop (succ i) xs
这是Python code:
的直接翻译def perms(A):
if len(A)==1:
yield A
for i in xrange(len(A)-1,-1,-1):
for B in perms(A[:i]+A[i+1:]):
yield B+A[i:i+1]
Python代码有效,但Haskell代码进入无限递归。列表理解中的permutations (delete i xs)
应该使流程更接近基础案例。为什么会发生无限递归?
修改: @augustss说:
当你有一个基于列表的函数的多个基本案例时,请务必注意。
所以我从
改变了基本情况permutations [] = []
permutations (x:[]) = [[x]]
更简单
permutations [] = [[]]
答案 0 :(得分:4)
你的循环不一样。
i <- [len,len-1..0]
VS
for i in xrange(len(A)-1,-1,-1):
第一种情况,你将i
绑定到长度,而不是长度减去1。结果是delete i xs
返回xs
,因此您将获得无限递归。
我也有几个旁注。
首先!!
是线性时间。编写一个将!!
,delete
和输入上的迭代组合成一个列表遍历的辅助函数会好得多。像select :: [a] -> [(a, [a])]
这样的东西。你可以有效地做到这一点。
其次,++
也是线性时间。使用它将单个元素附加到现有列表很慢。如果您的目标只是生成所有排列而不是它们的特定排序,那么您应该使用(xs !! i) : ys
作为返回的表达式。 (适合修改以响应第一点所做的任何更改。)
答案 1 :(得分:1)
select
基于@Carl's answer我实施了select :: [a] -> [(a, [a])]
功能。它的任务是生成一个元组列表(a, [a])
,其中元组的第一部分是列表中的元素,而元组的第二部分是列表中的所有元素,除了该元素。
select :: [a] -> [(a, [a])]
select [] = []
select (x:xs) = select' x [] xs
where
select' x left [] = [(x, left)]
select' x left right@(r:rs) = (x, left++right) : select' r (x:left) rs
但我发现在Haskell Libraries mailing list select
上更简单的select :: [a] -> [(a,[a])]
select [] = []
select (x:xs) = (x,xs) : [(y,x:ys) | (y,ys) <- select xs]
实施:
[(y,x:ys) | (y,ys) <- select xs]
map (\(y,ys) -> (y,x:ys)) (select2 xs)
map (second (x:)) (select2 xs)
请注意,这3个是等效的(second
是来自Control.Arrow
的函数):
select
以下是如何使用select [1,2,3] -- [(1,[2,3]),(2,[1,3]),(3,[2,1])]
的示例:
select
在我实施[a] -> [(a, [a])]
之前,我尝试在Hayoo中找到类型为permutations
的函数,各种库中有多个实现:
removeEach
package utility-ht
picks
package HaskellForMaths
来自parts'
个套件的{li>为hmt-diagrams
extractEachElem
package rosso
extractElem
package spaceprobe
select
问题是,我们的uncurry (:)
还不足以生成所有排列。我们可以使用(a, [a]) -> [a]
使用map (uncurry (:)) (select [1,2,3]) -- [[1,2,3],[2,1,3],[3,2,1]]
来构建每个元组的两个部分,但我们只得到一些排列,而不是全部:
select [1,2,3]
很清楚为什么,[(1,[2,3]),(2,[1,3]),(3,[2,1])]
会创建一个列表(1, [2,3])
,但我们必须置换子列表,这也是每个元组的第二部分!换句话说,如果我们有(1, [3,2])
,我们也必须添加select :: [a] -> [(a,[a])]
select [] = []
select (x:xs) = (x,xs) : map (\(y,ys) -> (y,x:ys)) (select xs)
permutations :: [a] -> [[a]]
permutations [] = [[]]
permutations xs = [cons s2 | s <- select2 xs, s2 <- subpermutations s]
where cons :: (a, [a]) -> [a]
cons = uncurry (:)
subpermutations :: (a, [a]) -> [(a, [a])]
subpermutations (x,xs) = map (\e -> (x, e)) $ permutations xs
。
查找列表所有排列的完整代码如下:
Data.List.permutations
请注意,我们函数的排列顺序与Data.List.permutations
不同。我们的函数具有词典顺序,而permutations [1,2,3] -- [[1,2,3],[2,1,3],[2,3,1],[1,3,2],[3,1,2],[3,2,1]]
Data.List.permutations [1,2,3] -- [[1,2,3],[2,1,3],[3,2,1],[2,3,1],[3,1,2],[1,3,2]]
没有
permutations
最后,如果我们进一步简化select :: [a] -> [(a,[a])]
select [] = []
select (x:xs) = (x,xs) : map (\(y,ys) -> (y,x:ys)) (select xs)
permutations :: [a] -> [[a]]
permutations [] = [[]]
permutations xs = [ y:zs | (y,ys) <- select xs, zs <- permutations ys]
函数,我们会得到Rosetta Code处的实现:
Data.List.permutations
另请注意,使用基于插入方法的Rosetta Code的实现与1 --> 2 --> 3
具有相同(非词典)顺序。
FWIW,来自包scc :: [(a, [a])] -> [[a]]
的功能uhc-util
,其中包含图表的strongly connected components。元组的第一部分是顶点,第二部分是所有顶点,顶点的边缘到达顶点。 IOW,图表[(1, [2]), (2, [3])]
变为scc [(1,[2,3,4]),(2,[1,3,4]),(3,[2,1,4]),(4,[3,2,1])] -- [[3,4], [1,2]]
。
@ManyToMany(mappedBy = "offices")
public List<Project> projects = new ArrayList<>();