微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在集合中找到整数符号的组合使得集合总和为 0 的算法

如何解决在集合中找到整数符号的组合使得集合总和为 0 的算法

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

如何有效地解决这个问题?基于类似的问题,我想某种动态编程是有序的。有没有关于这个特定问题的文献(我找不到它)。

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

解决方法

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

如果有办法求和到数组总和的一半,那么我们可以将所有这些数字设置为负数。其余的数字将为正数。由于这些子集中的每一个总和为总和的一半,因此它们各自的总和将为 0。

C++代码如下:

#include<stdio.h>

int arr[] = {1,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;
}

此代码的输出将是集合中正数和负数组合的最接近的可能总和为 0。

2 * max_half - sum 可以从 max_half - (sum - max_half) 推导出来,这将是我们最好的总和减去其余数字。

以下是不同数字集及其各自输出的一些示例:

设置:{1,4},输出:0

设置:{1,1,1},输出:-1

设置:{5,6,8,9,2},输出:0

设置:{1,50},输出:-49


子集求和问题网上有many explanations,这里就不解释了。

这段代码的时间复杂度为O(n * sum),空间复杂度为O(n * sum)。

也可以通过使用一维 dp 数组来牺牲一些时间复杂度来提高空间复杂度。

,

鉴于问题似乎是 NP 完全的, 使用 SAT、MILP、CP 或 ASP 求解器是最佳选择, 因为它们是为解决此类问题而量身定制的。

解决方案

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

给定文件 instance.lp

value(12).
value(12).
value(1).
value(2).
value(3).
value(5).
value(6).
value(7).

和文件 encoding.lp:

% 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).

问题可以用clingo解决, potassco 工具集的 ASP 求解器(可通过 conda、pip、Ubuntu Package Manger 等轻松安装...)。

调用:

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 和 CDNL
  • 声明式(你只描述问题,而不是如何解决它)
  • 易于扩展,具有其他约束
  • 还能够进行各种优化(例如优化最大的子集以形成总和)

编辑 你也可以复制粘贴这两个文件的内容,自己在线查看结果,使用clingo here

的js编译

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。