【问题标题】:How to Create the Power Set (Combinations) of the Infinite Set in F# using Sequences?如何使用序列在 F# 中创建无限集的幂集(组合)?
【发布时间】:2012-01-10 00:07:26
【问题描述】:

这是我在这个问题上的失败尝试,如果有任何帮助,我们将不胜感激。

我试图为在急切列表上工作的电源组提出最佳算法。这部分似乎工作正常。我遇到问题的部分是将其翻译为与序列一起使用,以便它可以在流式\无限列表上运行它。我真的不喜欢 yield 语法可能是因为我不太了解它,但我宁愿拥有它而不使用 yield 语法。

//All Combinations of items in a list
//i.e. the Powerset given each item is unique
//Note: lists are eager so can't be used for infinite
let listCombinations xs =
    List.fold (fun acc x ->
        List.collect (fun ys -> ys::[x::ys]) acc) [[]] xs

//This works fine (Still interested if it could be faster)
listCombinations [1;2;3;4;5] |> Seq.iter (fun x -> printfn "%A" x)

//All Combinations of items in a sequence
//i.e. the Powerset given each item is unique
//Note: Not working
let seqCombinations xs =
    Seq.fold (fun acc x ->
        Seq.collect (fun ys -> 
            seq { yield ys
                  yield seq { yield x
                              yield! ys} }) acc) Seq.empty xs

//All Combinations of items in a sequence
//i.e. the Powerset given each item is unique
//Note: Not working (even wrong type signature)
let seqCombinations2 xs =
    Seq.fold (fun acc x ->
        Seq.collect (fun ys ->
            Seq.append ys (Seq.append x ys)) acc) Seq.empty xs

//Sequences to test on
let infiniteSequence = Seq.initInfinite (fun i -> i + 1)
let finiteSequence = Seq.take 5 infiniteSequence

//This should work easy since its in a finite sequence
//But it does not, so their must be a bug in 'seqCombinations' above
for xs in seqCombinations finiteSequence do
    for y in xs do
        printfn "%A" y

//This one is much more difficult to get to work
//since its the powerset on the infinate sequence
//None the less If someone could help me find a way to make this work
//This is my ultimate goal
let firstFew = Seq.take 20 (seqCombinations infiniteSequence)
for xs in firstFew do
    for y in xs do
        printfn "%A" y

【问题讨论】:

  • 在一组大小为 N 的 powerset 中有 2^N 个元素,因此即使对于一组只有 60 个元素的集合,您看到的 powerset 大小约为 10^18 个元素,您'永远无法完全列举。通过使用序列而不是列表,您可能会使用更少的内存,但如果您能更好地解释您的目标,将会很有帮助。
  • 我的目标是看看我可以在 f# 中将惰性 eval 序列推到多远,这似乎是一个简单但又足够复杂的问题。我想以绝对最懒惰的方式制作无限集的幂集,同时在代码中保持一些优雅和良好的性能。
  • 这很好,但考虑到即使是中等大小的集合的 powerset 中的元素数量也非常多,如果你想用结果,但您尚未指定订单。
  • 您建议以什么顺序返回值。它们可以以可索引的方式返回吗?实际上可以仅根据输入计算索引吗?谢谢。
  • 我想不出一个具体的例子,但也许你正在寻找一个服从某些属性的最小集合(你知道存在一个)。当然,对于无限集的幂集,您不能按严格递增的顺序进行(或者您只会在枚举单元素子集的面颊解决方案中重现 Daniel 的舌头),但您可能会找到一些顺序你知道会命中你关心的实例。

标签: visual-studio math f# functional-programming


【解决方案1】:

您的seqCombinations 几乎是正确的,但您没有正确地将其从列表转换为序列。 [[]] 的等价物不是Seq.empty,而是Seq.singleton Seq.empty

let seqCombinations xs =
    Seq.fold (fun acc x ->
        Seq.collect (fun ys -> 
            seq { yield ys
                  yield seq { yield x
                              yield! ys} }) acc) (Seq.singleton Seq.empty) xs

上面的代码适用于有限序列。但是对于无限序列,它不起作用,因为它首先尝试到达终点,而对于无限序列,它显然永远不会这样做。

如果你想要一个可以处理无限序列的函数,我想出了两种方法,但它们都不是特别好。其中之一使用可变状态:

let seqCombinations xs =
    let combs = ref [[]]
    seq {
        yield! !combs
        for x in xs do
            let added = List.map (fun ys -> x::ys) !combs
            yield! added
            combs := !combs @ added
    }

另外一个就是过多处理seq<T>的细节:

open System.Collections.Generic

let seqCombinations (xs : seq<_>) =
    let rec combs acc (e : IEnumerator<_>) =
        seq {
            if (e.MoveNext()) then
                let added = List.map (fun ys -> (e.Current)::ys) acc
                yield! added
                yield! combs (acc @ added) e }

    use enumerator = xs.GetEnumerator()
    seq {
        yield []
        yield! combs [[]] enumerator
    }

我认为,如果您可以将无限序列视为头尾,就像 F# 中的有限列表或 Haskell 中的任何序列一样,这会容易得多。但肯定有可能在 F# 中有一种很好的方式来表达这一点,我只是没找到。

【讨论】:

  • 非常感谢,这解决了我的有限序列问题。你对让它支持无限序列有什么建议吗?或者如何在没有 yield 语法的情况下做到这一点?谢谢。
  • 非常感谢,这正是我正在寻找的。如果有更清洁的东西出现,我会很感兴趣。谢谢。
  • @AaronStainback 看看这个无限的斐波那契数序列的要点,它不使用列表,因为这可能效率低下,因为在列表末尾追加需要重建整个列表: gist.github.com/1159257。出于效率原因,Seq.cache 很重要。 let rec 绑定会产生一个警告,因为这不是一个函数。
【解决方案2】:

我最近在Generate powerset lazily 上问了一个类似的问题,并得到了一些不错的答案。

对于有限集的幂集,@Daniel 在上述链接中的答案是一个有效的解决方案,可能适合您的目的。你可以拿出一个测试用例来比较他和你的方法。

关于无限集的幂集,这里有一些数学知识。根据Cantor's theorem可数无限集的幂集是不可数无限的。这意味着即使以惰性方式也无法枚举所有整数(可数无限)的幂集。实数的直觉是相同的。由于实数是不可数无限的,我们实际上无法使用无限序列对其进行建模。

因此,没有算法可以枚举可数无限集的幂集。或者那种算法根本没有意义。

【讨论】:

  • 所有解决方案似乎都适用于list&lt;T&gt;,而不是seq&lt;T&gt;,因此您不能将它们用于无限序列(至少不能直接使用)。
  • 你是对的。 IMO,找到无限序列的幂集有点奇怪。将幂集建模为列表的惰性序列(或有限序列)似乎更合理。
  • 我同意这很奇怪,但这就是问题所在。
  • 太好了,奇怪的是我想要的。试图了解必须编写每个函数来处理获得无限输入是什么感觉。感谢您的帮助。
  • 哇,这真的可以用我永远无法形容的语言表达,它是无限的。感谢您的信息。
【解决方案3】:

这有点像玩笑,但实际上会为无限序列生成正确的结果(只是无法证明——经验上,而不是数学上)。

let powerset s =
  seq {
    yield Seq.empty
    for x in s -> seq [x]
  }

【讨论】:

  • 以什么方式计算值?当我尝试将代码更新为您的定义时,以下 seqCombinations infiniteSequence 失败,类型 seq 无法转换为 int。我真希望我能把它弄得这么小。谢谢。
  • @AaronStainback:就像我说的,这有点儿笑话。由于 powerset(S) 包括空集,而 S 本身的结果——只要你愿意观察它——是正确的。
  • 正如kvb所说,由于结果只能部分枚举,不指定顺序就没有意义。
  • 非常感谢您的这个“笑话”,我明白您的意思,现在订单非常重要。我也知道这是如何作弊 :) 这让我更好地理解了这个主题,谢谢。无论如何以您知道的某种有意义的方式索引这些结果?因此,您可以只吐出 x 和 y,它会从原始序列中弹出项目编号。他们对权力集有一些公认的有意义的顺序吗?谢谢。
  • 我不是专家,但根据Wikipedia articleinclusion 是一种常见的顺序(这个“解决方案”满足)。
猜你喜欢
  • 2013-11-22
  • 2016-01-09
  • 1970-01-01
  • 2015-10-09
  • 2011-02-10
  • 2019-02-19
  • 2012-07-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多