优化子集和实现

Posted

技术标签:

【中文标题】优化子集和实现【英文标题】:Optimizing subset sum implementation 【发布时间】:2011-06-04 12:14:42 【问题描述】:

我正在使用以下代码解决子集和问题的变体。这个问题需要从一个更大的集合(超集)中生成 11 个整数的子集,并检查它是否匹配特定的值(endsum)。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int endsum = 0, supersetsize = 0, done = 0;
int superset[] = 1,30,10,7,11,27,3,5,6,50,45,32,25,67,13,37,19,52,18,9;
int combo = 0;

int searchForPlayerInArray(int arr[], int player) 
    for (int i=0; i<11; i++) 
        if (arr[i] == player) 
            return 1;
        
    
    return 0;


int sumOfArray(int arr[]) 
    int res = 0;
    for (int i=0; i<11; i++) 
        res+=arr[i];
    
    return res;


void printArray(int arr[], int arrSize) 
    for (int j=0; j<arrSize; j++) 
        printf("%2d ",arr[j]);
    
    printf("= %d\n",endsum);


void permute(int subset[], int pos, int sspos) 
    if (done)  //when a correct solution has been found, stop recursion
        return;
    
    if (sspos == supersetsize)  // out of possible additions
        return;
    
    if (pos == 11)  //is the current subset 11 ints long?
        int res = sumOfArray(subset);
        combo++;
        if (res == endsum)  //if the sum of the array matches the wanted sum, print
            printArray(subset,11);
            done = 1;
        
        return;
    
    for (int i=sspos; i<supersetsize; i++) 
        //assert(pos < 11);
        //assert(i+1 <= supersetsize);
        subset[pos] = superset[i];
        permute(subset,pos+1,i+1);
    


int main(void) 
    endsum = 110;
    supersetsize = 20;
    int *arr;
    arr = malloc(supersetsize*sizeof(int));
    int i;
    for (i=0; i<supersetsize; i++) 
        arr[i] = 0;
    

    permute(arr,0,0);

    printf("Combinations: %d",combo);
    return 0;

尽管此解决方案适用于小型超集 (

编辑:根据大众需求添加的完整源代码。

【问题讨论】:

这是在 C++ 还是 C 中?它像 C 一样臭,但从技术上讲,我猜它可能是 C++。 这对我来说似乎是标准的子集和。它是否正确?如果是这样,我建议自己做更多的研究。在 *** 和其他地方有很多关于这个问题的讨论。像骗子一样关闭? @AaronMcDaid 非标准部分是缺少负整数和非零答案。 啊,是的,非零答案可以表示为负数,以便将其表示为标准子集和问题? 【参考方案1】:

仅生成唯一子集的一种方法是按顺序添加超集中的元素,并使用permute 的附加参数(例如supersetPos)来指示您在超集中的位置。这会生成唯一的排序排列。

编辑:AFAIK 在您的示例上正确运行的代码:

#include <stdio.h>

int superset[] = 
 1, 30, 10, 7, 11,
 27, 3, 5, 6, 50,
 45, 32, 25, 67, 13,
 37, 19, 52, 18, 9
;
int supersetsize = 20;
int endsum = 110;
int done = 0;

int sumOfArray(int array[]) 
  int sum = 0;
  for(int i = 0; i < 11; i++)
      sum += array[i];
  return sum;


void permute(int subset[], int pos, int sspos) 

    if (pos == 11)  //is the current subset 11 ints long?
        if (sumOfArray(subset) == endsum)  //if the sum of the array matches the wanted sum, print
            for (int j=0; j<11; j++) 
                printf("%d ",subset[j]);
            
            printf("\n");
            done = 1;
        
        return;
    
    for (int i=sspos; i<supersetsize; i++) 
        subset[pos] = superset[i];
        permute(subset,pos+1,i+1);
        if (done)  //when a correct solution has been found, stop recursion
            return;
        
    


int main() 
  int subset[11] = 0;
  permute(subset, 0, 0);

【讨论】:

我从这个解决方案中遇到了分段错误,也许你在数组边界之外写? @Gorkamorka 我不知道,因为您没有发布完整的示例,也没有将supersetsize 作为显式参数传递。但是i 永远不会大于supersetsize 并且subset 的所有访问都是从您的代码中显式复制的。你能发个 sscce (sscce.org) 我尝试了以下输入: endsum = 110 supersetsize = 20 superset = 1 30 10 7 11 27 3 5 6 50 45 32 25 67 13 37 19 52 18 9 正确子集 = 1 10 7 11 27 3 5 6 13 18 9 没有输出结果,但程序在 XP 下设法完成。在 Ubuntu 下仍然存在段错误。 @Gorkamorka:好的,但是 1) 你能发布一个可编译的例子,以便 可以测试它吗? 2) 它是否与您的示例代码正确运行? 我不打算从 rapidshare 下载你的代码,你为什么不简单地编辑你的问题以包含相关代码?【参考方案2】:

我认为没有办法在比指数时间更好的时间内生成唯一子集。

要有效地解决子集和,您需要使用动态规划。有一些用于子集和的伪多项式时间算法以这种方式工作。这个Wikipedia article 可能会有所帮助。

【讨论】:

绝对没有办法在次指数时间内生成唯一的子集,因为子集的数量是指数级的。但至少可以避免在超指数时间内产生所有排列。【参考方案3】:

你可以试试我的代码(我试着只给出一个伪代码而不是完全解决你的作业):

// array is all the numbers you are looking from them
// length is the number of arrays
// pos is the position of the slot you are going to fill
// max is nomber of slots you have to fill (in your case since you are going for the 11 sets you have to set this value as 11
// sum is the sum of all the values selected until now
// searchbegin is the first element you can pick from your array (I'm using this variable to only generate subarrays of the superset (array))
// target is the targetvalue you are looking for.

void generate_all(int []array, int length, int pos,int max, int sum,int searchbegin,int target)

    if max = pos
        if sum = target
            printselectedresults();
    for i:searchbegin->length-max+pos
        if (sum + array[i] < target)
        
            addtoresults(i);
            generate_all(array,length,pos+1,max,sum+array[i],i+1,target);
            removefromresults(i);
        

有了所有这些信息,我认为您可以轻松地将该代码实现为您的目标语言并使用它。

在我的函数中,所有生成的排列都是超集的子数组,因此不能生成两次排列,而且每个排列都至少生成一次。

【讨论】:

以上是关于优化子集和实现的主要内容,如果未能解决你的问题,请参考以下文章

优化文件夹子集的 gsutil 下载

从dfs暴力->记忆化->子集背包dp->滚动数组优化

有哪些算法/优化可用于计算数组中元素的条件子集?

bzoj3687简单题(dp+bitset优化)

深度学习——优化算法

优化sybase中的条件查询