好吧,我我希望等到你回答了我的問題,但是我非常有趣地找出答案,我只需要將它寫出來並分享。書呆子狙擊,我想!我確信我不是第一個發明了下面算法的人,但我希望你喜歡這個演講。
我們的第一步是給出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」。此外,上述分組中的每個列表都表現出相似的結構:第一組中的列表有三組,每組兩列!作爲它們的第二個元素,每個包含1
,2
和3
;這些組中的每個列表都有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)
分配/時間少於千分之一。看起來像一場勝利!
相關:http://stackoverflow.com/questions/20641772/haskell-function-for-finding-letter-number – raymonad
你有什麼想法如何解決這個問題?他們哪裏錯了? –
對不起@raymonad。當我發佈沒有真正算法的問題時,我應該寫。它一定很快。搜索蠻力是**不**快。 (有問題的話,permut使用懶惰的「蠻力」,並且工作正常......但是也不是很好)。 –