可逆置换算法

时间:2013-12-17 18:17:57

标签: algorithm haskell

列表排列的哪种算法是可预测的?

例如,我可以得到第i个排列的数量

(Haskell代码)

--List of all possible permutations
permut [] = [[]]
permut xs = [x:ys|x<-xs,ys<-permut (delete x xs)]

--In command line call:
> permut "abc" !! 2
"bac"

但我不知道如何扭转它。 我想要这样的事情:

> getNumOfPermut "abc" "bac"
2

任何可逆的算法都会发生! 提前谢谢!

2 个答案:

答案 0 :(得分:2)

好吧,我想等到你回答我关于你尝试过的问题的问题,但我得到了很多乐趣,我只需要写下来并分享它。我猜是书呆子狙击!我确信我不是第一个发明下面算法的人,但我希望你喜欢这个演示文稿。

我们的第一步是提供permut的实际可运行实现(您尚未完成)。我们的实现策略将是一个简单的:选择列表中的一些元素,选择剩余元素的一些排列,并将两者连接起来。

chooseFrom [] = []
chooseFrom (x:xs) = (x,xs) : [(y, x:ys) | (y, ys) <- chooseFrom xs]

permut [] = [[]]
permut xs = do
    (element, remaining) <- chooseFrom xs
    permutation <- permut remaining
    return (element:permutation)

如果我们在样本列表上运行它,那么它的行为非常清楚:

> permut [1..4]
[[0,1,2,3],[0,1,3,2],[0,2,1,3],[0,2,3,1],[0,3,1,2],[0,3,2,1],[1,0,2,3],[1,0,3,2],[1,2,0,3],[1,2,3,0],[1,3,0,2],[1,3,2,0],[2,0,1,3],[2,0,3,1],[2,1,0,3],[2,1,3,0],[2,3,0,1],[2,3,1,0],[3,0,1,2],[3,0,2,1],[3,1,0,2],[3,1,2,0],[3,2,0,1],[3,2,1,0]]

结果有很多结构;例如,如果我们按所包含列表的第一个元素进行分组,则有四个组,每个组包含6个(即3个!)元素:

> mapM_ print $ groupBy ((==) `on` head) it
[[0,1,2,3],[0,1,3,2],[0,2,1,3],[0,2,3,1],[0,3,1,2],[0,3,2,1]]
[[1,0,2,3],[1,0,3,2],[1,2,0,3],[1,2,3,0],[1,3,0,2],[1,3,2,0]]
[[2,0,1,3],[2,0,3,1],[2,1,0,3],[2,1,3,0],[2,3,0,1],[2,3,1,0]]
[[3,0,1,2],[3,0,2,1],[3,1,0,2],[3,1,2,0],[3,2,0,1],[3,2,1,0]]

原来如此!列表的第一个数字告诉我们“要添加多少6个”。此外,上述分组中的每个列表都具有相似的结构:第一组中的列表有三组,每组2个!每个元素包含123作为其第二个元素;每个组中的列表有2组1!每个元素都以每个剩余数字开头;每个那些组都有1组0!每个元素都以唯一的剩余数字开头。所以第二个数字告诉我们“要添加多少2个”,第三个数字告诉我们“要添加多少个1”,最后一个数字告诉我们“要添加多少个1”(但总是告诉我们添加0个1)

如果您之前在数字上实现了基数更改功能(例如十进制到十六进制或类似),您可能会识别此模式。实际上,我们可以将此视为基础变换操作的滑动基础:而不是1s,10s,100s,1000s等列,我们有0!s,1!s,2!s,3! s,4!s等列。我们来写吧!为了提高效率,我们将使用factorials函数预先计算所有滑动基础。

import Data.List

factorials n = scanr (*) 1 [n,n-1..1]
deleteAt i xs = case splitAt i xs of (b, e) -> b ++ drop 1 e

permutIndices permutation original
    = go (factorials (length permutation - 1))
         permutation
         original
    where
    go _ [] [] = [0]
    go _ [] _  = []
    go _ _  [] = []
    go (base:bases) (x:xs) ys = do
        i <- elemIndices x ys
        remainder <- go bases xs (deleteAt i ys)
        return (i*base + remainder)
    go [] _ _ = error "the impossible happened!"

这是一个样本健全性检查:

> map (`permutIndices` [1..4]) (permut [1..4])
[[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23]]

而且,为了好玩,在这里你可以看到它正确处理歧义:

> permutIndices "acbba" "aabbc"
[21,23,45,47]
> map (permut "aabbc"!!) it
["acbba","acbba","acbba","acbba"]

...并且表明它比elemIndices明显更有效:

> :set +s
> elemIndices "zyxwvutsr" (permut "rstuvwxyz")
[362879]
(2.65 secs, 1288004848 bytes)
> permutIndices "zyxwvutsr" "rstuvwxyz"
[362879]
(0.00 secs, 1030304 bytes)

分配/时间不到千分之一。好像赢了!

答案 1 :(得分:1)

所以,要明确的是,你正在寻找一种方法来找到给定渗透的位置 -

"bac"

在给定的权利列表中 -

["abc", "acb", "bac", ....]

这个问题实际上并没有与permutions本身有关。您想要在数组中找到元素的位置。

正如@raymonad在评论中提到的那样,stackoverflow.com/questions/20641772 /处理这个问题,并且答案是,使用elemIndex

elemIndex thePermutionToFind $ permut theString

请记住,如果字母重复,输出中的值可能会出现多次,如果“permut”函数不会删除这些重复项(即注意permut“aa”= [“aa”, “aa”])....在这种情况下,elemIndices函数将有用。

如果elemIndex返回Nothing,则表示您提供的字符串不是permtion。

(对于大字符串来说,这不是最有效的算法,因为permutions的数量增长就像字符串大小的阶乘......这比指数更糟。)