【问题标题】:Calculating permutations in F#在 F# 中计算排列
【发布时间】:2008-11-13 07:21:54
【问题描述】:

questionanswer 的启发,如何在 F# 中创建通用排列算法?谷歌没有给出任何有用的答案。

编辑:我在下面提供了我的最佳答案,但我怀疑 Tomas 的更好(当然更短!)

【问题讨论】:

    标签: algorithm f# permutation


    【解决方案1】:

    你也可以这样写:

    let rec permutations list taken = 
      seq { if Set.count taken = List.length list then yield [] else
            for l in list do
              if not (Set.contains l taken) then 
                for perm in permutations list (Set.add l taken)  do
                  yield l::perm }
    

    “list”参数包含您想要置换的所有数字,“taken”是一个包含已使用数字的集合。当所有数字都取完时,该函数返回空列表。 否则,它会遍历所有仍然可用的数字,获取剩余数字的所有可能排列(递归地使用 'permutations')并在返回 (l::perm) 之前将当前数字附加到每个数字。

    要运行它,你会给它一个空集,因为开头没有使用数字:

    permutations [1;2;3] Set.empty;;
    

    【讨论】:

    • 仅供参考 - Set.mem 已重命名为 Set.contains
    • 看起来您的解决方案不允许原始列表中的重复值
    【解决方案2】:

    我喜欢这个实现(但不记得它的来源):

    let rec insertions x = function
        | []             -> [[x]]
        | (y :: ys) as l -> (x::l)::(List.map (fun x -> y::x) (insertions x ys))
    
    let rec permutations = function
        | []      -> seq [ [] ]
        | x :: xs -> Seq.concat (Seq.map (insertions x) (permutations xs))
    

    【讨论】:

    • 这看起来真不错。这可以转换为不同排列的版本吗?请参阅下面我自己的解决方案,该解决方案看起来不如您的解决方案。谢谢。
    • 希望你能记住来源。就速度而言,这比我尝试过的所有其他排列函数都快。
    • @rick-minerich 这与stackoverflow.com/questions/1526046/f-permutations/… 几乎相同,尽管 IMO 更清楚一点...
    • AFAIR,大约 10 年前,在我访问哈斯凯尔兰兹期间,我已经看到了这个实现(即使在那时它也不是“全新的”)。可能“真正的来源”更古老,这看起来像是一本正经的大学资料。
    • 另外,Seq.concat (Seq.map (insertions x) (permutations xs)) 可以替换为 Seq.collect (insertions x) (permutations xs)
    【解决方案3】:

    Tomas 的解决方案非常优雅:它简短、纯粹是功能性的,而且很懒惰。我认为它甚至可能是尾递归的。此外,它还按字典顺序产生排列。但是,我们可以在内部使用命令式解决方案将性能提高两倍,同时仍向外部公开功能接口。

    函数permutations 采用通用序列e 以及通用比较函数f : ('a -> 'a -> int),并按字典顺序懒惰地产生不可变的排列。比较函数允许我们生成不一定是comparable 的元素的排列,以及轻松指定反向或自定义排序。

    内部函数permutehere 描述的算法的命令式实现。转换函数let comparer f = { new System.Collections.Generic.IComparer<'a> with member self.Compare(x,y) = f x y } 允许我们使用System.Array.Sort 重载,它使用IComparer 进行就地子范围自定义排序。

    let permutations f e =
        ///Advances (mutating) perm to the next lexical permutation.
        let permute (perm:'a[]) (f: 'a->'a->int) (comparer:System.Collections.Generic.IComparer<'a>) : bool =
            try
                //Find the longest "tail" that is ordered in decreasing order ((s+1)..perm.Length-1).
                //will throw an index out of bounds exception if perm is the last permuation,
                //but will not corrupt perm.
                let rec find i =
                    if (f perm.[i] perm.[i-1]) >= 0 then i-1
                    else find (i-1)
                let s = find (perm.Length-1)
                let s' = perm.[s]
    
                //Change the number just before the tail (s') to the smallest number bigger than it in the tail (perm.[t]).
                let rec find i imin =
                    if i = perm.Length then imin
                    elif (f perm.[i] s') > 0 && (f perm.[i] perm.[imin]) < 0 then find (i+1) i
                    else find (i+1) imin
                let t = find (s+1) (s+1)
    
                perm.[s] <- perm.[t]
                perm.[t] <- s'
    
                //Sort the tail in increasing order.
                System.Array.Sort(perm, s+1, perm.Length - s - 1, comparer)
                true
            with
            | _ -> false
    
        //permuation sequence expression 
        let c = f |> comparer
        let freeze arr = arr |> Array.copy |> Seq.readonly
        seq { let e' = Seq.toArray e
              yield freeze e'
              while permute e' f c do
                  yield freeze e' }
    

    现在为方便起见,我们有以下let flip f x y = f y x

    let permutationsAsc e = permutations compare e
    let permutationsDesc e = permutations (flip compare) e
    

    【讨论】:

      【解决方案4】:

      我最新的最佳答案

      //mini-extension to List for removing 1 element from a list
      module List = 
          let remove n lst = List.filter (fun x -> x <> n) lst
      
      //Node type declared outside permutations function allows us to define a pruning filter
      type Node<'a> =
          | Branch of ('a * Node<'a> seq)
          | Leaf of 'a
      
      let permutations treefilter lst =
          //Builds a tree representing all possible permutations
          let rec nodeBuilder lst x = //x is the next element to use
              match lst with  //lst is all the remaining elements to be permuted
              | [x] -> seq { yield Leaf(x) }  //only x left in list -> we are at a leaf
              | h ->   //anything else left -> we are at a branch, recurse 
                  let ilst = List.remove x lst   //get new list without i, use this to build subnodes of branch
                  seq { yield Branch(x, Seq.map_concat (nodeBuilder ilst) ilst) }
      
          //converts a tree to a list for each leafpath
          let rec pathBuilder pth n = // pth is the accumulated path, n is the current node
              match n with
              | Leaf(i) -> seq { yield List.rev (i :: pth) } //path list is constructed from root to leaf, so have to reverse it
              | Branch(i, nodes) -> Seq.map_concat (pathBuilder (i :: pth)) nodes
      
          let nodes = 
              lst                                     //using input list
              |> Seq.map_concat (nodeBuilder lst)     //build permutations tree
              |> Seq.choose treefilter                //prune tree if necessary
              |> Seq.map_concat (pathBuilder [])      //convert to seq of path lists
      
          nodes
      

      permutations 函数的工作原理是构建一个 n 叉树,表示传入的“事物”列表的所有可能排列,然后遍历树以构建列表列表。使用“Seq”可以显着提高性能,因为它可以让一切变得懒惰。

      permutations 函数的第二个参数允许调用者在生成路径之前定义一个过滤器,用于“修剪”树(参见下面的示例,我不想要任何前导零)。

      一些示例用法:Node 是通用的,因此我们可以对“任何东西”进行排列:

      let myfilter n = Some(n)  //i.e., don't filter
      permutations myfilter ['A';'B';'C';'D'] 
      
      //in this case, I want to 'prune' leading zeros from my list before generating paths
      let noLeadingZero n = 
          match n with
          | Branch(0, _) -> None
          | n -> Some(n)
      
      //Curry myself an int-list permutations function with no leading zeros
      let noLZperm = permutations noLeadingZero
      noLZperm [0..9] 
      

      (特别感谢Tomas Petricek,欢迎任何cmets)

      【讨论】:

      • 请注意,F# 有一个 List.permute 函数,但这并不完全相同(我不确定它实际上做了什么......)
      【解决方案5】:

      如果您需要不同的排列(当原始集合有重复时),您可以使用:

      let rec insertions pre c post =
          seq {
              if List.length post = 0 then
                  yield pre @ [c]
              else
                  if List.forall (fun x->x<>c) post then
                      yield pre@[c]@post
                  yield! insertions (pre@[post.Head]) c post.Tail
              }
      
      let rec permutations l =
          seq {
              if List.length l = 1 then
                  yield l
              else
                  let subperms = permutations l.Tail
                  for sub in subperms do
                      yield! insertions [] l.Head sub
              }
      

      这是对this C# 代码的直接翻译。我愿意接受有关更实用的外观和感觉的建议。

      【讨论】:

        【解决方案6】:

        看看这个:

        http://fsharpcode.blogspot.com/2010/04/permutations.html

        let length = Seq.length
        let take = Seq.take
        let skip = Seq.skip
        let (++) = Seq.append
        let concat = Seq.concat
        let map = Seq.map
        
        let (|Empty|Cons|) (xs:seq<'a>) : Choice<Unit, 'a * seq<'a>> =
            if (Seq.isEmpty xs) then Empty else Cons(Seq.head xs, Seq.skip 1 xs)
        
        let interleave x ys =
            seq { for i in [0..length ys] ->
                    (take i ys) ++ seq [x] ++ (skip i ys) }
        
        let rec permutations xs =
                    match xs with
                    | Empty -> seq [seq []]
                    | Cons(x,xs) -> concat(map (interleave x) (permutations xs))
        

        【讨论】:

          【解决方案7】:

          如果您需要重复排列,这是使用 List.indexed 而不是元素比较的“按书本”方法在构造排列时过滤掉元素。

          let permutations s =
              let rec perm perms carry rem =
                  match rem with
                      | [] -> carry::perms
                      | l ->
                          let li = List.indexed l
                          let permutations =
                                  seq { for ci in li ->
                                          let (i, c) = ci
                                          (perm
                                                  perms
                                                  (c::carry)
                                                  (li |> List.filter (fun (index, _) -> i <> index) |> List.map (fun (_, char) -> char))) }
          
                          permutations |> Seq.fold List.append []
              perm [] [] s
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-01-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多