在 Erlang 或 Ruby 中生成集合的幂集而不保留堆栈

发布于 2024-12-21 08:09:13 字数 351 浏览 0 评论 0原文

我想生成一个相当大的集合(大约 30-50 个元素)的幂集,并且我知道需要 2^n 来存储幂集。

是否可以一次生成一个子集?

即通过迭代生成一个集合的幂集,将每个生成的子集保存到磁盘/数据库,将其从堆栈/内存中删除,然后才继续生成其他子集?

不幸的是,我未能修改 ErlangRuby 满足我的需求的示例。

I would like to generate a powerset of a rather big set (about 30-50 elements) and I know that it takes 2^n to store the powerset.

Is it possible to generate one subset at a time?

I.e. generate a powerset of a set with iterations, saving each generated subset to disk/database, removing it from the stack/memory and only then continuing to generate other subsets?

Unfortunately I have failed to modify Erlang and Ruby examples to my needs.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦幻的心爱 2024-12-28 08:09:13

编辑:如果没有给出块,则添加枚举器(如@Jörg W Mittag)。

class Array
  def powerset
    return to_enum(:powerset) unless block_given?
    1.upto(self.size) do |n|
      self.combination(n).each{|i| yield i}
    end
  end
end
# demo
['a', 'b', 'c'].powerset{|item| p item} # items are generated one at a time
ps = [1, 2, 3, 4].powerset # no block, so you'll get an enumerator 
10.times.map{ ps.next } # 10.times without a block is also an enumerator

输出

["a"]
["b"]
["c"]
["a", "b"]
["a", "c"]
["b", "c"]
["a", "b", "c"]
[[1], [2], [3], [4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

Edit: Added the enumerator (as @Jörg W Mittag) if no block is given.

class Array
  def powerset
    return to_enum(:powerset) unless block_given?
    1.upto(self.size) do |n|
      self.combination(n).each{|i| yield i}
    end
  end
end
# demo
['a', 'b', 'c'].powerset{|item| p item} # items are generated one at a time
ps = [1, 2, 3, 4].powerset # no block, so you'll get an enumerator 
10.times.map{ ps.next } # 10.times without a block is also an enumerator

Output

["a"]
["b"]
["c"]
["a", "b"]
["a", "c"]
["b", "c"]
["a", "b", "c"]
[[1], [2], [3], [4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
意犹 2024-12-28 08:09:13

生成列表幂集的一种方法(实际上是您的 Erlang 示例使用的方法)是迭代从 0 到 2^n(不包括)的所有数字 x,并且对于每个 x,当且仅当 x 的第 i 位时,生成包含原始列表的第 i 个元素的列表> 已设置。

由于使用此方法生成当前列表仅取决于 x 的值,而不取决于任何先前生成的列表,因此您不必在使用列表后将它们保留在内存中。所以这个方法可以用来做你想做的事。

One way to generate a powerset of a list (which is in fact the one that your Erlang example uses) is to iterate over all numbers x from 0 to 2^n (exclusive) and for each x, generate the list that contains the ith element of the original list if and only if the ith bit of x is set.

Since using this approach generating the current list only depends on the value of x and not on any of the previously generated lists, you don't have to keep the lists in memory after using them. So this approach can be used to do what you want.

同尘 2024-12-28 08:09:13

它使用标准的“位数组”技巧来生成幂集(并且它使用了 Ruby 的 Integer 表现为位数组的事实)。但更重要的是,它使用 Enumerator 来延迟生成集合。

require 'set'

module Enumerable
  def powerset
    number_of_sets = 2 ** count

    Enumerator.new {|ps|
      number_of_sets.times {|i|
        ps << Set[*reject.with_index {|_, j| i[j].zero? }]
      }
    }
  end
end

即使对于数千个元素,这也能完美工作:

enum = (1..10_000).powerset
enum.next # => #<Set: {}>
enum.next # => #<Set: {1}>
enum.next # => #<Set: {2}>
enum.next # => #<Set: {1, 2}>
enum.next # => #<Set: {3}>
enum.next # => #<Set: {1, 3}>
enum.next # => #<Set: {2, 3}>
enum.next # => #<Set: {1, 2, 3}>
enum.next # => #<Set: {4}>
enum.next # => #<Set: {1, 4}>
enum.next # => #<Set: {2, 4}>
enum.next # => #<Set: {1, 2, 4}>
enum.next # => #<Set: {3, 4}>
enum.next # => #<Set: {1, 3, 4}>
enum.next # => #<Set: {2, 3, 4}>
enum.next # => #<Set: {1, 2, 3, 4}>
enum.next # => #<Set: {5}>
# ...

编辑:这是基于@steenslag的解决方案。我完全忘记了 Array#combination,因为我太专注于寻找适用于任何 Enumerable 的解决方案。但是,我的解决方案要求 Enumerable 无论如何都是有限的,并且任何有限的 Enumerable 都应该可以表示为 Array,所以这并不是什么问题。一个限制。

module Enumerable
  def powerset
    ary = to_a

    Enumerator.new {|ps|
      ary.size.times {|n|
        ary.combination(n).each(&ps.method(:yield))
      }
    }
  end
end

This uses the standard "bit array" trick for generating power sets (and it uses the fact that Ruby's Integers behave as bit arrays). But more importantly, it uses an Enumerator to generate the sets lazily.

require 'set'

module Enumerable
  def powerset
    number_of_sets = 2 ** count

    Enumerator.new {|ps|
      number_of_sets.times {|i|
        ps << Set[*reject.with_index {|_, j| i[j].zero? }]
      }
    }
  end
end

This works perfectly fine even for thousands of elements:

enum = (1..10_000).powerset
enum.next # => #<Set: {}>
enum.next # => #<Set: {1}>
enum.next # => #<Set: {2}>
enum.next # => #<Set: {1, 2}>
enum.next # => #<Set: {3}>
enum.next # => #<Set: {1, 3}>
enum.next # => #<Set: {2, 3}>
enum.next # => #<Set: {1, 2, 3}>
enum.next # => #<Set: {4}>
enum.next # => #<Set: {1, 4}>
enum.next # => #<Set: {2, 4}>
enum.next # => #<Set: {1, 2, 4}>
enum.next # => #<Set: {3, 4}>
enum.next # => #<Set: {1, 3, 4}>
enum.next # => #<Set: {2, 3, 4}>
enum.next # => #<Set: {1, 2, 3, 4}>
enum.next # => #<Set: {5}>
# ...

EDIT: This is based on @steenslag's solution. I totally forgot about Array#combination, since I was too focused on finding a solution that would work for any Enumerable. However, my solution requires that the Enumerable be finite anyway, and any finite Enumerable should probably be representable as an Array, so that's not much of a restriction.

module Enumerable
  def powerset
    ary = to_a

    Enumerator.new {|ps|
      ary.size.times {|n|
        ary.combination(n).each(&ps.method(:yield))
      }
    }
  end
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文