我知道“一个powerset只是0到2 ^ N-1之间的任何数字,其中N是集合成员的数量,而二进制表示中的一个表示存在相应的成员”。
我想使用从二进制表示到实际集合元素的映射生成一个powerset。
我如何使用Erlang进行此操作?
我尝试修改this,但没有成功。
UPD:我的目标是编写一个迭代算法,在不保持堆栈的情况下生成集合的powerset。我倾向于认为二进制表示可以帮助我。
Here是Ruby中的成功解决方案,但我需要在Erlang中编写它。
UPD2: Here是伪代码的解决方案,我想在Erlang中做类似的事情。
答案 0 :(得分:3)
首先,我要注意,对于Erlang,递归解决方案并不一定意味着它将消耗额外的堆栈。当一个方法是尾递归时(即它最后做的是递归调用),编译器会重新编写它来修改参数,然后跳转到方法的开头。这对于函数式语言来说是相当标准的。
要生成所有数字A到B的列表,请使用库方法lists:seq(A, B)
。
要将值列表(例如列表从0到2 ^ N-1)转换为另一个值列表(例如从其二进制表示生成的集合),请使用lists:map
或{{ 3}}
不是将数字拆分成二进制表示,您可能需要考虑转换它并检查是否通过生成电源列表在每个M值(0到2 ^ N-1)中设置相应的位 - 的-2-位掩码。然后,您可以执行二进制AND以查看该位是否已设置。
将所有这些放在一起,你得到一个解决方案,如:
generate_powerset(List) ->
% Do some pre-processing of the list to help with checks later.
% This involves modifying the list to combine the element with
% the bitmask it will need later on, such as:
% [a, b, c, d, e] ==> [{1,a}, {2,b}, {4,c}, {8,d}, {16,e}]
PowersOf2 = [1 bsl (X-1) || X <- lists:seq(1, length(List))],
ListWithMasks = lists:zip(PowersOf2, List),
% Generate the list from 0 to 1^N - 1
AllMs = lists:seq(0, (1 bsl length(List)) - 1),
% For each value, generate the corresponding subset
lists:map(fun (M) -> generate_subset(M, ListWithMasks) end, AllMs).
% or, using a list comprehension:
% [generate_subset(M, ListWithMasks) || M <- AllMs].
generate_subset(M, ListWithMasks) ->
% List comprehension: choose each element where the Mask value has
% the corresponding bit set in M.
[Element || {Mask, Element} <- ListWithMasks, M band Mask =/= 0].
但是,您也可以使用尾递归来实现相同的功能,而不会占用堆栈空间。它也不需要在0到2 ^ N-1之间生成或保持列表。
generate_powerset(List) ->
% same preliminary steps as above...
PowersOf2 = [1 bsl (X-1) || X <- lists:seq(1, length(List))],
ListWithMasks = lists:zip(PowersOf2, List),
% call tail-recursive helper method -- it can have the same name
% as long as it has different arity.
generate_powerset(ListWithMasks, (1 bsl length(List)) - 1, []).
generate_powerset(_ListWithMasks, -1, Acc) -> Acc;
generate_powerset(ListWithMasks, M, Acc) ->
generate_powerset(ListWithMasks, M-1,
[generate_subset(M, ListWithMasks) | Acc]).
% same as above...
generate_subset(M, ListWithMasks) ->
[Element || {Mask, Element} <- ListWithMasks, M band Mask =/= 0].
请注意,在生成子集列表时,您需要将新元素放在列表的开头。列表是单链接且不可变的,因此如果您想将元素放在任何位置而不是开头,则必须更新“下一个”指针,这会导致列表被复制。这就是帮助函数将Acc
列表放在尾部而不是Acc ++ [generate_subset(...)]
的原因。在这种情况下,由于我们倒计时而不是向上,我们已经倒退了,所以最终会以相同的顺序出现。
所以,总之,
lists:map
的变体来惯用的。[NewElement | ExistingList]
)。