Algorithm 求集合中整数符号组合的算法,使集合和为0

Algorithm 求集合中整数符号组合的算法,使集合和为0,algorithm,dynamic-programming,complexity-theory,np,Algorithm,Dynamic Programming,Complexity Theory,Np,给定一组S的n正整数,我们想知道是否可以为S(+或-)中的每个数字找到一个符号组合,使得S的和为0 如何有效地解决这个问题?基于类似的问题,我认为某种动态规划是正确的。有没有关于这个具体问题的文献(我很难找到它) 我想这与子集和问题类似。但是,现在我们必须使用整个集合,对于每个整数,si,我们可以包含-si或+si,但不能同时包含两者。这个问题的解决方案涉及子集和问题 如果有一种方法可以求和到数组总和的一半,那么我们可以将所有这些数字都设置为负数。剩下的数字将是正数。由于这些子集的总和为总总和的

给定一组Sn正整数,我们想知道是否可以为S(+或-)中的每个数字找到一个符号组合,使得S的和为0

如何有效地解决这个问题?基于类似的问题,我认为某种动态规划是正确的。有没有关于这个具体问题的文献(我很难找到它)


我想这与子集和问题类似。但是,现在我们必须使用整个集合,对于每个整数,si,我们可以包含-si+si,但不能同时包含两者。

这个问题的解决方案涉及子集和问题

如果有一种方法可以求和到数组总和的一半,那么我们可以将所有这些数字都设置为负数。剩下的数字将是正数。由于这些子集的总和为总总和的一半,因此它们各自的总和将为0

以下是c++中的代码:

#include<stdio.h>

int arr[] = {1, 2, 2, 3, 4};
int n = 5; // size of arr
int sum = 0;

// dp array only needs to be [n + 1][total sum + 1] big
bool dp[30][100];
inline void subset_sum(){
    for (int i = 0; i <= sum; i++)
        dp[0][i] = false;

    for (int i = 0; i <= n; i++)
        dp[i][0] = true;

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= sum; j++) {
            dp[i][j] = dp[i - 1][j];
            if (arr[i - 1] <= j)
                dp[i][j] |= dp[i - 1][j - arr[i - 1]];
        }
    }
}
int main(){
    for (int i = 0; i < n; i++)
        sum += arr[i];

    // run subset sum dp using a bottom-up approach
    // True = sum is possible, False = not possible
    subset_sum();

    int max_half;
    for (int i = sum / 2; i>=1; i--){
        if (dp[n][i]){ // it is possible to sum to i using values in arr
            max_half = i;
            break;
        }
    }

    // output will be the closest sum of positives
    // and negatives to 0
    printf("%d\n", 2 * max_half - sum);

    return 0;
}
#包括
int arr[]={1,2,2,3,4};
int n=5;//arr的大小
整数和=0;
//dp阵列只需要[n+1][总和+1]大
bool dp[30][100];;
内联无效子集_sum(){

对于(int i=0;i,假设问题似乎是NP完全问题, 使用SAT、MILP、CP或ASP解算器是最佳选择, 因为这些都是为解决这类问题而量身定做的

解决方案

下面是一个使用ASP(答案集编程)的解决方案

给定一个文件
instance.lp

value(12).
value(12).
value(1).
value(2).
value(3).
value(5).
value(6).
value(7).
% every value can be positive (or not)
{pos(X)} :- value(X).

% fail if the sum is not 0
:- not 0 = #sum {V : pos(V); -V : not pos(V), value(V)}.

% format output
#show pos/1.
#show neg(V) : not pos(V), value(V).
以及文件
encoding.lp

value(12).
value(12).
value(1).
value(2).
value(3).
value(5).
value(6).
value(7).
% every value can be positive (or not)
{pos(X)} :- value(X).

% fail if the sum is not 0
:- not 0 = #sum {V : pos(V); -V : not pos(V), value(V)}.

% format output
#show pos/1.
#show neg(V) : not pos(V), value(V).
这个问题可以用, 工具集合的ASP解算器(可通过conda、pip、Ubuntu包管理器等轻松安装)

电话:

clingo instance.lp encoding.lp
为您提供以下结果:

Answer: 1
pos(1) pos(2) pos(3) pos(5) pos(7) neg(6) neg(12)
您可以使用以下方法列举所有可能的解决方案:

clingo instance.lp encoding.lp 0
给你

Answer: 1
pos(1) pos(2) pos(3) pos(5) pos(7) neg(6) neg(12)
Answer: 2
pos(2) pos(3) pos(6) pos(7) neg(5) neg(1) neg(12)
Answer: 3
pos(5) pos(6) pos(7) neg(3) neg(2) neg(1) neg(12)
Answer: 4
pos(12) pos(1) pos(2) pos(3) neg(7) neg(6) neg(5)
Answer: 5
pos(12) pos(6) neg(7) neg(5) neg(3) neg(2) neg(1)
Answer: 6
pos(12) pos(1) pos(5) neg(7) neg(6) neg(3) neg(2)
ASP

使用ASP解决此问题具有以下优点:

  • 易于维护(问题描述非常简短,易于阅读)
  • 非常快(基于SAT和
  • 声明性(您只描述问题,而不是如何解决问题)
  • 易于使用其他约束进行扩展
  • 还能够进行各种优化(如优化最大子集以形成总和)
编辑
您也可以使用js编译的
clingo

复制并粘贴两个文件的内容,自己在线查看结果。这个想法是有道理的,但在尝试了一些东西之后,似乎有些错误。除了dp[6]超出范围(我想应该是dp[5]),生成的dp数组是[0,4,1,3,1,2],这意味着不存在组合。但是,我们可以清楚地形成组合1+2-2+3-4。知道发生了什么吗?仔细研究一下,我选择使用的dp条件可能不是这种情况下的最佳选择。它不仅仅取决于值与0的接近程度。同时,我仍在尝试想出一个更好的条件我终于找到了这个问题的解决方案,并编辑了我的答案。请看一看。