【问题标题】:Generate combinations of integers by least sum通过最小和生成整数组合
【发布时间】:2013-05-28 15:46:15
【问题描述】:

我的问题源于生成一个非常大的素数排序列表的唯一组合选择 5,但我需要返回组合,以便首先返回总和最小的组合。 python itertools.combinations() 函数返回数字,增加最后一个直到它到达可迭代对象的末尾,然后再增加下一个,等等。这不适合我的项目,因为总和将继续增加直到它到达最后一个元素在我的一组素数中,总和会在再次增加之前下降。

例如,如果我有一小组素数 {2,3,5,7,11,13,17,19,23,29},我需要按以下顺序返回组合:

    (2, 3, 5, 7, 11)    sum = 28
    (2, 3, 5, 7, 13)    sum = 30
    (2, 3, 5, 7, 17)    sum = 34
    (2, 3, 5, 11, 13)   sum = 34
    (2, 3, 5, 7, 19)    sum = 36
    (2, 3, 7, 11, 13)   sum = 36
    (2, 3, 5, 11, 17)   sum = 38
    (2, 5, 7, 11, 13)   sum = 38
    (3, 5, 7, 11, 13)   sum = 39
    (2, 3, 5, 7, 23)    sum = 40
    (2, 3, 5, 11, 19)   sum = 40
    (2, 3, 5, 13, 17)   sum = 40
    (2, 3, 7, 11, 17)   sum = 40
    (2, 3, 5, 13, 19)   sum = 42
    (2, 3, 7, 11, 19)   sum = 42
    (2, 3, 7, 13, 17)   sum = 42
    (2, 5, 7, 11, 17)   sum = 42
    ...

具有相同和的两个集合的顺序无关紧要,只要生成器不会在具有较小和的集合之前返回具有较大和的集合。我正在使用的素数集包含大约 100,000 个元素,这意味着简单地生成所有组合并对它们进行排序是非常不可行的,因为它需要 83,325,000,291,662,500,020,000 个元组的空间,每个元组有 5 个整数。此外,返回的组合元组中的每个元素都必须是唯一的;不能有重复的整数。有什么想法吗?

【问题讨论】:

标签: python sum combinations primes


【解决方案1】:

不要生成组合并将它们相加,而是反过来尝试 - 给定一个和序列,为每个和生成组合:

# some primes for tesing
primes = [2]
x = 3
while x < 100000:
    if all(x % p for p in primes):
        primes.append(x)
    x += 2

# main code

def find(tsum, tlen):

    def _find(tsum, tlen, path, idx):
        if tlen <= 0:
            if tsum == 0:
                yield path
            return
        while True:
            p = primes[idx]
            if p > tsum:
                return
            for f in _find(tsum - p, tlen - 1, path + [p], idx + 1):
                yield f
            idx += 1

    return _find(tsum, tlen, [], 0)

for s in range(1001, 1002): # just for testing, should be range(28, max possible sum)
    for comb in find(s, 5):
        print s, comb

这在性能方面远非理想,但在我的机器上仍然相当快。

【讨论】:

  • 我想这原则上是对像素数这样的密集集合执行此操作的最佳方法,对于任何给定的总和,您可以期待很多解决方案。但是您应该选择并测试元组中的最后一个数字而不进行搜索,并特别处理 2,因为它的存在决定了总和是偶数还是奇数。
  • @starblue:当然,有很多可能的优化,但我想保持代码简单,以说明这个想法。
【解决方案2】:

我不确定这需要多少空间,所以最好先用一组较小的素数尝试一下。

  • 有一个数组/素数列表,按升序排序。

  • 保留一个索引五元组的优先级队列,以对应素数之和为key/weight/priority/whaddayacallit,初始填充权重为2+3+5+7+11 = 28的五元组(0,1,2,3,4)

  • 虽然队列不为空,

    1. 从队列中获取总和最小的五元组,例如(a, b, c, d, e, f)(当然,删除它)。
    2. 将它的直接后继插入队列,后继是那些

      • (a+1,b,c,d,e)
      • (a,b+1,c,d,e)
      • (a,b,c+1,d,e)
      • (a,b,c,d+1,e)
      • (a,b,c,d,e+1)

      严格升序且最后一个组件小于素数列表的长度

    3. yield 先前得到的五元组

【讨论】:

  • 假设这是使用以(2,3,5,7,11,13,17,19...) 开头的素数列表进行测试的,则应返回的第一个元组将是(2,3,5,7,11),其索引为(0,1,2,3,4)。除非我误解了您的答案,否则不会通过将每个索引增加一个来添加后继者会导致单个值的倍数出现在每个元组中吗?我刚刚更新了我的答案,以指定组合没有替换,以防在这方面不明确。
  • @matt18224 是的,您需要一个只有唯一元素的优先级队列。根据 PQ 的实现,您需要通过在 PQ 中已经有一组元组来自己处理这个问题,并且仅在它们尚未在其中时才添加子项(您可以在集合中删除每个元组)从队列中弹出)。但是当你让它生成很多元组时,队列变得相当大,所以必须以某种方式延迟将元组放入其中。不知道有没有好办法。
【解决方案3】:

如果它可以帮助你,这是一个用 C 语言编写的程序,它几乎通过最小和生成整数组合。整数必须按升序给出。本程序继承自this post中的其他程序:

#include <stdio.h>
#include <stdlib.h>
int v[100], stack[100];
int sp,i,j,k,n,g,s;
int main()
{
    printf("Dimension of V:");
    scanf( "%d",&n);
    //Input numbers
    for (i=0 ; i<n ; i++) {
        printf("V[%d]=",i);
        scanf( "%d",&g);
        v[i]=g;
    }
    printf("subset number:");
    scanf( "%d",&k);
    printf("running...\n");
    j=0;
    s=0;
    sp=-1;
    while(1) {
        // stack ones until stack overflow
        while(sp<n-1 && j<k && n-sp>k-j)  {
            j=j+1;
            sp=sp+1;
            stack[sp]=1;
            s=s+v[sp];
            if(j==k) {
                for (i=0 ; i<=sp ; i++) if (stack[i]==1) printf("%2d ",v[i]);
                printf("sum=%d\n",s);
            }
        }
        // unstack all the zeros until top of stack is equal one
        while (stack[sp]==0 && sp>=0) sp=sp-1;
        // if Bottom of stack is reached then stop
        if (sp<0) break;
        // set top of stack from one to zero
        stack[sp]=0;
        s=s-v[sp];
        j=j-1;
    }
    return 0;
}

【讨论】:

    猜你喜欢
    • 2019-05-16
    • 1970-01-01
    • 1970-01-01
    • 2011-12-30
    • 2012-04-13
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多