2014-01-28 54 views
1

我有一個給定的列表,例如[2, 3, 5, 587],我想要一個完整的組合列表。所以想要像[2, 2*3,2*5, 2*587, 3, 3*5, 3*587, 5, 5*587, 587]。由於我在Haskell的初學者水平上,我很好奇列表操作是怎麼樣的。Haskell:整數的列表組合

此外,我很好奇,如果計算的基礎列表可能會很昂貴,這將如何影響功能的成本? (如果我將承擔名單有極限值,即< 20)

雷姆:列表的順序可以事後做,但我也實在沒有線索,如果是這樣的功能或之後內便宜。

+1

排列組合就意味着所有的列表中的元素,但在不同的順序打亂。你確定你不是指「組合」嗎?另外,你爲什麼只獲得1或2個元素的組,而不是3或4的組? – hugomg

+0

是的,你是對的,沒有排列,更多的組合。你也是對的,雖然沒有明確提及,但三種組合的組是有意義的。但我想,根據建議,這應該是最後調整它的一小步。無論如何,你的問題/疑惑給了我一點思考'我想達成什麼?'。謝謝。 – LeO

+0

[本SO問題](http://stackoverflow.com/questions/21265454/subsequences-of-length-n-from-list-performance/21268310)包含關於在Haskell生成組合了很好的討論。 – ErikR

回答

2

因此,看來你想要的是列表中的所有對產品:

ghci> :m +Data.List 
ghci> [ a * b | a:bs <- tails [2, 3, 5, 587], b <- bs ] 
[6,10,1174,15,1761,2935] 

但你也想inital號:

ghci> [ a * b | a:bs <- tails [2, 3, 5, 587], b <- 1:bs ] 
[2,6,10,1174,3,15,1761,5,2935,587] 

這將使用列表理解,但是這可能也可以通過定期列表操作完成:

ghci> concatMap (\a:bs -> a : map (a*) bs) . init $ tails [2, 3, 5, 587] 
[2,6,10,1174,3,15,1761,5,2935,587] 

後者有點容易解釋:

  • Data.List.tails產生一個列表的所有後綴:

    ghci> tails [2, 3, 5, 587] 
    [[2,3,5,587],[3,5,587],[5,587],[587],[]] 
    
  • Prelude.init從列表中滴的最後一個元素。在這裏,我使用它來刪除空白後綴,因爲在下一步中會導致錯誤的處理。

    ghci> init [[2,3,5,587],[3,5,587],[5,587],[587],[]] 
    [[2,3,5,587],[3,5,587],[5,587],[587]] 
    
    ghci> init $ tails [2, 3, 5, 587] 
    [[2,3,5,587],[3,5,587],[5,587],[587]] 
    
  • Prelude.concatMap運行在一個列表的每個元素的功能,並且將結果組合成扁平列表。所以

    ghci> concatMap (\a -> replicate a a) [1,2,3] 
    [1, 2, 2, 3, 3, 3] 
    
  • \(a:bs) -> a : map (a*) bs做了幾件事情。我的論點

    1. 我模式匹配,聲稱它的匹配列表與至少一個元素(這就是爲什麼我init放棄了空列表),配件初始元素爲a和後來的元素融入bs
    2. 這產生了具有相同的第一元件作爲參數a:列表,但
    3. 乘以amap (a*) bs)每個後來元件。
+0

OP後來在評論中澄清說他們想要所有的組合,而不僅僅是配對。這可能是爲了從素數分解中產生一個數的所有因數。 :) –

+0

正確的意圖 - 良好的猜測,但給定的方法是錯誤的;)例如2 * 2 * 587不會導致除數4(!)但是給定的解決方案對於列表操作是很好的 - 這是我的目標之一,看看它是如何工作的。 – LeO

1

你可以使用Data.List.tails列表的後綴。 這給你一個列表的列表,那麼你可以做你想做的內乘這個名單上有類似的功能:

prodAll [] = [] 
prodAll (h:t) = h:(map (* h) $ t) 

然後,您可以映射在每個內部列表此功能並連接結果:

f :: Num a => [a] -> [a] 
f = concat . map prodAll . tails 
6

其他人解釋瞭如何做對,所以我在這裏關心自己與獲得的組合。

如果你想的所有長度的組合,這只是你的名單power set,並且可以計算方式如下:

powerset :: [a] -> [[a]] 
powerset (x:xs) = let xs' = powerset xs in xs' ++ map (x:) xs' 
powerset []  = [[]] 

-- powerset [1, 2] === [[],[2],[1],[1,2]] 
-- you can take the products: 
-- map product $ powerset [1, 2] == [1, 2, 1, 2] 

有哈斯克爾替代powerset實現人們心目中的排序的經典:

import Control.Monad 

powerset = filterM (const [True, False]) 

你可以看看filterMsource,看看它是如何工作本質上是相同的方式爲行吟詩人以上爲powerset

在另一方面,如果你想有一定規模的所有組合,你可以做到以下幾點:

combsOf :: Int -> [a] -> [[a]] 
combsOf n _ | n < 1 = [[]] 
combsOf n (x:xs) = combsOf n xs ++ map (x:) (combsOf (n - 1) xs) 
combsOf _ _   = [] 

-- combsOf 2 [1, 2, 3] === [[2,3],[1,3],[1,2]] 
+0

+1。最後一個也讓人想起SICP的「改變」。 :) ---一個相關的問題,雖然沒有被問到:如果在(有序)輸入中允許重複,產生所有獨特的組合。 –

+0

@WillNess:我也很想回想起硬幣更換問題; [這裏](https://gist.github.com/AndrasKovacs/8686121),我前一段制定出來的,如果你有興趣一漂亮和快速的解決方案:) –