Python 生成生成生成集的算法

Python 生成生成生成集的算法,python,algorithm,Python,Algorithm,给定此输入:[1,2,3,4] 我想生成生成集: [1] [2] [3] [4] [1] [2] [3,4] [1] [2,3] [4] [1] [3] [2,4] [1,2] [3] [4] [1,3] [2] [4] [1,4] [2] [3] [1,2] [3,4] [1,3] [2,4] [1,4] [2,3] [1,2,3] [4] [1,2,4] [3] [1,3,4] [2] [2,3,4] [1] [1,2,3,4] 每个集合都有原始集合的所有元素,它们排列成唯一的子集。产生这

给定此输入:[1,2,3,4]

我想生成生成集:

[1] [2] [3] [4]
[1] [2] [3,4]
[1] [2,3] [4]
[1] [3] [2,4]
[1,2] [3] [4]
[1,3] [2] [4]
[1,4] [2] [3]
[1,2] [3,4]
[1,3] [2,4]
[1,4] [2,3]
[1,2,3] [4]
[1,2,4] [3]
[1,3,4] [2]
[2,3,4] [1]
[1,2,3,4]
每个集合都有原始集合的所有元素,它们排列成唯一的子集。产生这些集合的算法是什么?我尝试过使用choose、permutation、combination、powerset等Python生成器函数,但无法获得正确的组合

2009年1月20日

这不是一个家庭作业问题。这是我为www.projecteuler.net问题118所做的改进答案。我已经有了一个缓慢的解决方案,但找到了一个更好的方法——除了我不知道如何生成集

当我从一个就职聚会回来的时候,我会把我的密码贴出来

2009年1月21日

这是我最终使用的算法:

def spanningsets(items):
    if len(items) == 1:
        yield [items]
    else:
        left_set, last = items[:-1], [items[-1]]
        for cc in spanningsets(left_set):
            yield cc + [last]
            for i,elem in enumerate(cc):
                yield cc[:i] + [elem + last] + cc[i+1:]
@尤瓦尔F:我知道怎么做动力装置。下面是一个简单的实现:

def powerset(s) :
    length = len(s)
    for i in xrange(0, 2**length) :
        yield [c for j, c in enumerate(s) if (1 << j) & i]
    return
def动力装置:
长度=长度(s)
对于X范围内的i(0,2**长度):

yield[c代表j,c在枚举(s)中,如果(1这是怎么回事?我还没有测试它,但我稍后会尝试

我认为这种技术叫做动态规划:

  • 取第一个元素
    [1]

    您可以用它创建什么?只有
    [1]

  • 拿第二个
    [2]

    现在有两种可能性:
    [1,2]
    [1][2]

  • 拿第三个
    [3]

    使用数字2的第一个
    [1,2]
    可以创建
    [1,2,3]
    [1,2][3]

    使用数字2的第二个
    [1][2]
    可以创建
    [1,3][2]
    [1][2,3]
    [1][2][3]


  • 我希望我试图展示的内容足够清晰。(如果没有,请发表评论!)

    这应该可以,尽管我还没有对它进行足够的测试

    def spanningsets(items):
        if not items: return
        if len(items) == 1:
            yield [[items[-1]]]
        else:
            for cc in spanningsets(items[:-1]):
                yield cc + [[items[-1]]]
                for i in range(len(cc)):
                    yield cc[:i] + [cc[i] + [items[-1]]] + cc[i+1:]
    
    for sset in spanningsets([1, 2, 3, 4]):
        print ' '.join(map(str, sset))
    
    输出:

    [1] [2] [3] [4]
    [1, 4] [2] [3]
    [1] [2, 4] [3]
    [1] [2] [3, 4]
    [1, 3] [2] [4]
    [1, 3, 4] [2]
    [1, 3] [2, 4]
    [1] [2, 3] [4]
    [1, 4] [2, 3]
    [1] [2, 3, 4]
    [1, 2] [3] [4]
    [1, 2, 4] [3]
    [1, 2] [3, 4]
    [1, 2, 3] [4]
    [1, 2, 3, 4]
    
    下面是关于这个问题的一页。也许您可以将其中一个代码引用翻译成python


    编辑:这是一个类似的问题。是关于生成分区的SUNY repository页面,我认为这是正确的问题。

    结果集和空集{}看起来像是(或幂集)的结果,但不是同一件事

    我写了一篇关于类似问题的文章,这个问题有一些实现(虽然是C#),在某些情况下更注重速度而不是清晰度。第一个例子应该很容易翻译。也许它会给出一些想法

    它们的工作原理是,对组合进行emumerating类似于二进制计数(想象从0到16计数)。您不说明顺序是否重要,也不只是生成所有组合,因此之后可能需要进行快速整理


    这里有一个(忽略这个奇怪的标题,讨论的方向是另一个)

    我认为下面的方法是生成euler问题的最佳方法,因为你可以用素数生成子集的数量来替换返回值,而乘法(尤其是记忆法)是很简单的:

    关键部分是确保递归端始终具有最左边的元素,这样就不会得到重复的元素

    我有一些乱七八糟的C#代码:

        IEnumerable<IEnumerable<List<int>>> GenerateSubsets(List<int> list)
        {
            int p = (1 << (list.Count)) - 2;
            List<int> lhs = new List<int>();
            List<int> rhs = new List<int>();
            while (p >= 0)
            {
                for (int i = 0; i < list.Count; i++)
                    if ((p & (1 << i)) == 0)
                        lhs.Add(list[i]);
                    else
                        rhs.Add(list[i]);
    
                if (rhs.Count > 0)
                    foreach (var rhsSubset in GenerateSubsets(rhs))
                        yield return Combine(lhs, rhsSubset);
                else
                    yield return Combine(lhs, null);
    
                lhs.Clear();
                rhs.Clear();
                p -= 2;
            }
        }
    
        IEnumerable<List<int>> Combine(List<int> list, IEnumerable<List<int>> rest)
        {
            yield return list;
            if (rest != null)
                foreach (List<int> x in rest)
                    yield return x;
        }
    
    IEnumerable生成子集(列表)
    {
    int p=(1=0)
    {
    for(int i=0;i
    对于haskell和ruby来说,这也是一个很好的问题,对于清晰的示例+1。每个步骤的一般规则是“将元素放入一个现有子集中,或放入一个新的子集中”。这很清楚,但只是为了记录,它不是动态编程。DP是指采用缓慢(如指数时间)的递归算法和记录(“备忘录”)部分解决方案,以便可重复使用,从而实现更快的解决方案(例如O(n^2))算法。如果你为问题的最小子集创建解决方案,那么动态规划不就是问题的第二个最小子集吗?gs:哎呀,我说得太快了。是的,如果你重用通过解决较小的子问题生成的子集,而不是重新生成它们,那就是DP。我很高兴知道我还没有完成你误解了DP:)+1:参考已知正确且完整的算法。这是相关的,但不是同一个问题。幂集不是问题的答案。已编辑。只是出于兴趣,生成集和幂集之间有什么区别?示例的结果将是相同的,不是吗?我知道这并不意味着ut会对所有例子都适用。幂集是所有子集的集。生成集的每个成员都是一组集,其并集是原始集。例如,{1,2,3}的幂集是{{},{1},{2},{1,2},{3},{1,3},{2,3}。功率集中始终有2^n个集合,因为集合中的n个基本元素中的每一个都可以存在或不存在,就像n位字中的每一位都可以打开或关闭一样。第4行可以是:yield[项目]这是一个非常奇怪的生成器函数。我从来没有见过一个在外循环中有递归的函数,也没有见过一个在else stmt中有两个收益率的函数。我肯定要仔细考虑一下。感谢一个伟大的解决方案!@recursive:最初是这样的。我将它改为
    yield[[items[-1]]
    强调循环内收益率的相似性。顺便说一下,我认为最好只传递初始列表中的位置,而不是传递列表的整个部分
    spanningset(items[:-1])    IEnumerable<IEnumerable<List<int>>> GenerateSubsets(List<int> list)
        {
            int p = (1 << (list.Count)) - 2;
            List<int> lhs = new List<int>();
            List<int> rhs = new List<int>();
            while (p >= 0)
            {
                for (int i = 0; i < list.Count; i++)
                    if ((p & (1 << i)) == 0)
                        lhs.Add(list[i]);
                    else
                        rhs.Add(list[i]);
    
                if (rhs.Count > 0)
                    foreach (var rhsSubset in GenerateSubsets(rhs))
                        yield return Combine(lhs, rhsSubset);
                else
                    yield return Combine(lhs, null);
    
                lhs.Clear();
                rhs.Clear();
                p -= 2;
            }
        }
    
        IEnumerable<List<int>> Combine(List<int> list, IEnumerable<List<int>> rest)
        {
            yield return list;
            if (rest != null)
                foreach (List<int> x in rest)
                    yield return x;
        }