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