有效地生成“减法链”

时间:2016-03-17 00:41:16

标签: algorithm language-agnostic combinatorics

如果您想要一些上下文,我之前发布了another question。看来我采用这种方法走错了路。

Addition chains可用于最小化取幂数所需的乘法次数。例如, 7 需要四次乘法。两个计算 2 = a×a和a 4 = a 2 ×a 2 ,另外两个计算 7 = a 4 ×a 2 ×a。

同样,我正在尝试为一组数字生成所有可能的“减法链”。例如,给定一组数字{1, 2, 3},我试图生成以下排列。

{1, 2, 3}

{1, 2, 3}, {1, 2}
{1, 2, 3}, {1, 2}, {1}
{1, 2, 3}, {1, 2}, {2}
{1, 2, 3}, {1, 2}, {1}, {2}

{1, 2, 3}, {1, 3}
{1, 2, 3}, {1, 3}, {1}
{1, 2, 3}, {1, 3}, {3}
{1, 2, 3}, {1, 3}, {1}, {3}

{1, 2, 3}, {2, 3}
{1, 2, 3}, {2, 3}, {2}
{1, 2, 3}, {2, 3}, {3}
{1, 2, 3}, {2, 3}, {2}, {3}

{1, 2, 3}, {1, 2}, {1, 3}
{1, 2, 3}, {1, 2}, {1, 3}, {1}
{1, 2, 3}, {1, 2}, {1, 3}, {2}
{1, 2, 3}, {1, 2}, {1, 3}, {3}
{1, 2, 3}, {1, 2}, {1, 3}, {1}, {2}
{1, 2, 3}, {1, 2}, {1, 3}, {1}, {3}
{1, 2, 3}, {1, 2}, {1, 3}, {2}, {3}
{1, 2, 3}, {1, 2}, {1, 3}, {1}, {2}, {3}

# and so on...

通过从排列中的另一个集合中删除单个元素,可以找到排列中的每个元素(除{1, 2, 3}之外)。

例如,排列{1, 2, 3}, {1}无效,因为无法通过从{1}删除单个元素来构建{1, 2, 3}

是否有已知算法可以找到电源组功率集的这个子集?我的实现将在Python中,但问题是语言无关。另外,我实际上并不想要包含具有单个元素的集合的排列(例如{1, 2, 3}, {1, 2}, {1}),因为它们对应于不感兴趣的“独裁者”案例。

1 个答案:

答案 0 :(得分:1)

生成所有这些列表的算法可以按如下方式工作:对于当前列表中的每个集合,创建一个副本,删除一个元素,将其添加到列表中,然后递归调用算法。您还必须确保不生成重复项,这可以通过确保新列表“比较小”(通过(排序的)元素的长度或成对比较)而不是前一个来完成。

这是Python中的一个实现,作为生成器函数,没有太多优化。这似乎现在运行得很好,生成所有子集而没有任何重复。

def generate_sets(sets, min_num=2):
    yield sets
    added = set() # new sets we already generated in this iteration
    for set_ in sets:
        # only if the current set has the right length
        if min_num < len(set_) <= len(sets[-1]) + 1:
            for x in set_:
                # remove each element in turn (frozenset so we can put in into added)
                new = set_.difference({x})
                # prevent same subset being reachable form multiple sets
                frozen = frozenset(new)
                if frozen not in added:
                    added.add(frozen)
                    # recurse only if current element is "smaller" than last
                    if (len(new), sorted(new)) < (len(sets[-1]), sorted(sets[-1])):
                        for result in generate_sets(sets + [new], min_num):
                            yield result

对于generate_sets([{1,2,3}], min_num=2),这会生成以下列表:

[{1, 2, 3}]
[{1, 2, 3}, {2, 3}]
[{1, 2, 3}, {2, 3}, {1, 3}]
[{1, 2, 3}, {2, 3}, {1, 3}, {1, 2}]
[{1, 2, 3}, {2, 3}, {1, 2}]
[{1, 2, 3}, {1, 3}]
[{1, 2, 3}, {1, 3}, {1, 2}]
[{1, 2, 3}, {1, 2}]

对于generate_sets([{1,2,3}], 1),共生成45个集合列表。

但是,我没有看到与您之前问题的关联:{1, 2, 3}, {1, 2}{1, 2, 3}, {1, 3}{1, 2, 3}, {2, 3}是否应该被视为等效?