如何找出一组中的哪些数字与另一个给定的数字相加?

发布于 2024-07-29 18:54:57 字数 462 浏览 11 评论 0原文

这是我在使用会计系统时遇到的一个问题。

我有一组交易,但它们的总和不等于会计部门认为应有的金额。 他们不是质疑数学,只是质疑所包含的交易:p

是否有一种算法可以帮助我确定不应包含集合中的哪些交易,以便总和与给定金额匹配。

Given Set:  
2  
4  
5  
7

Given Sum Amount:
13

Result Set:
2
4
7

编辑: 该集中的交易数量少于 100 笔。 有没有人有一个 C# 示例,因为 解决 NP 完全问题上没有一个示例XKCD问题问题?

老兄,我应该获得计算机科学学位。

Here's a problem that I seem to be running into working with an accounting system.

I have a set of transactions, but their sum does not equal the amount that the accounting department thinks that it should. They are not questioning the math, just the transactions being included :p

Is there an algorithm that would help me determine which transactions in the set should not be included in order for the sum to match a given amount.

Given Set:  
2  
4  
5  
7

Given Sum Amount:
13

Result Set:
2
4
7

Edit:
There's less than 100 transactions in the set. Does anyone have a C# example as there is not one on the Solving the NP-complete problem in XKCD question?

Man, I should have gotten a CS degree.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

雨后彩虹 2024-08-05 18:54:57

这是子集和问题,即NP-Complete。 但这并不意味着没有找到子集和的算法。

This is the Subset Sum problem, which is NP-Complete. But that doesn't mean there isn't an algorithm for finding a subset sum.

把梦留给海 2024-08-05 18:54:57

这就是背包问题,它是NP完全问题。 除了小输入集之外,您无法轻松地用任何东西精确地解决它。 对于任何规模相当大的问题集来说,它都是一生中需要解决的问题之一。

也就是说,有遗传算法背包求解器。

This is the Knapsack Problem and it's NP-Complete. You won't easily solve it exactly with anything except small input sets. For any decent-sized problem set, it's one of those lifetime-of-the-universe-to-solve problems.

That said, there are genetic-algorithm knapsack solvers out there.

奈何桥上唱咆哮 2024-08-05 18:54:57

正如上面成员所说,这就是子集和问题(或背包问题)。
然而,说不能高效地完成这一点并不十分准确。 可以做到,只是做不到
在多项式时间内。 使用动态规划,解决方案实际上非常简单
和递归(并且在伪多项式时间内)。

给定整数 [a_1, ... ,a_n] 和数字 T,

定义数组 S[i,k] 来表示之间是否存在元素子集
a_1, ... a_i 加起来为 k。 (这是一个二进制矩阵)。

然后我们可以定义一个递归关系如下:

S[i,k] = S[i-1,k] 或 S[i-1,k-a_j]

换句话说,这意味着我们要么使用元素 a_i,要么不使用元素 a_i 。
答案位于 S[n,T]。

构建矩阵 S 的工作量是多少?
嗯,S 有 n*T 个元素。 要计算每个元素,
我们必须做 O(1) 的工作。 所以完整的运行
时间是O(n*T)。

现在看来,我已经证明了 P=NP,因为
似乎是多项式时间算法。 不过,请记住
我们以二进制形式测量输入大小,因此对于某些情况 T = 2^p
p。

我认为没有人会说上述解决方案,当
执行不当是不合理的。 事实上,对于很多人来说
合理的问题规模,它将表现出色。

另外,有一些启发式方法可以解决这个问题,但我是
不是该领域的专家。

As the above members have said, this is the Subset Sum problem (or knapsack problem).
However, to say it cannot be done efficiently is not very precise. It can be done, just not
in polynomial time. THe solution is actually quite simple using dynamic programming
and recursion (and in pseudo-polynomial time).

Given integers [a_1, ... ,a_n] and a number T,

Define the array S[i,k] to denote if there is a subset of elements between
a_1, ... a_i which add up to k. (This is a binary matrix).

We can then define a recursive relationship as follows:

S[i,k] = S[i-1,k] or S[i-1,k-a_j]

In words, this means we either use element a_i or we do not.
The answer will be located at S[n,T].

What is the work load to construct matrix S?
Well, S has n*T elements. To compute each element,
we must do O(1) work. So the complete running
time is O(n*T).

Now at this point, it seems that I have proven P=NP, as this
seems to be a polynomial time algorithm. However, remember
that we measure input size in binary, so T = 2^p for some
p.

I don't think anyone would say that the above solution, when
implemented properly is unreasonable. IN fact, for many
reasonable problem sizes, it will perform admirably.

Also, there are some heuristics for solving this problem, but I am
not an expert in that area.

陌若浮生 2024-08-05 18:54:57

这是背包问题的一个版本。 它是 NP 完全的,所以你不会得到一个好的通用答案。 您的交易集有多大? 是像你展示的那样 5 还是更像 500?

This is a version of the knapsack problem. It's NP complete, so you're not going to get a good general answer. How big are your sets of transactions? Is it 5 like you showed, or is it more like 500?

反差帅 2024-08-05 18:54:57

好的。 很多人都给出了问题的名称,并提到了 NP 问题的难度。 总的来说,它们是正确的。 但是,您有一个非常具体的案例需要解决。 要问的第一个问题是您是否认为您的 100 笔交易接近正确。 您有一些总计(“您的”总计)。 他们总共有一些。 (“真实”总计)。 因此,您的一些交易是伪造的。 如果您怀疑其中只有几笔虚假交易,那么情况还不错。 例如,让我们考虑只有一笔虚假交易的情况。 在这种情况下,我们只需检查 100 个不同的号码。 如果有 2 个伪造的反式,那么您将看到 100*99 的检查,如果有 4 个伪造的反式,事情就会变得疯狂,几乎有 100,000,000 个步骤。 不过,如果你所做的只是添加一些 int ,那也不算太糟糕。

另一个可能的捷径是检查数据的性质(顺便说一句,是否可以发布 100 个“数字”和预期总和?)。 你的总和比实际总和是多少? 是否有任何值太大以至于消除它们会使您的总和突然低于实际总和? 如果是这样,您就知道这些值不可能是假值。 例如,在您的示例中,7 是绝对必需的。

OK. Lots of people have given the name of the problem and mentioned how NP hard it is . And in general, they are correct. However, you have a very specific case you need to solve. The first question to ask is whether or not you think your 100 transactions are close to being the right ones. You have some total ("your" total). They have some total. ("real" total). Some of your transactions are therefore bogus. If you suspect that there are only a few bogus transactions in there, then this isn't so bad. For example, let's consider the case where there is only one bogus transaction. In that case, we'd only have to check 100 different numbers. If there are 2 bogus trans, then you're looking at 100*99 checks, and things will get crazy at 4 bogus trans, with almost 100,000,000 steps. though if all you're doing is adding some int's that's not too terrible.

Another possible shortcut is to examine the nature of your data (incidentally, is it possible to post the 100 "numbers" and the expected sum?). How much is your sum over the real sum? Are there any values so big that eliminating them would make your sum suddenly lower than the real sum? If so, you know those values cannot be the bogus ones. For example, in your example, 7 is absolutely required.

淡淡的优雅 2024-08-05 18:54:57
        bool bBreak = false;
        int iEnd = 13;
        ArrayList ar1 = new ArrayList();
        ar1.Add(2);
        ar1.Add(4);
        ar1.Add(5);
        ar1.Add(7);

        String s1 = " ";
        foreach (int i in ar1)
        {
            if (i == iEnd)
            {
                s1 = "Result = " + i;
                bBreak = true;
            }
            if (bBreak) break;
            ArrayList ar2 = new ArrayList();
            ar2.AddRange(ar1);
            ar2.Remove(i);
            foreach (int j in ar2)
            {
                if ((i + j) == iEnd)
                {
                    s1 = "Results = " + i + ", " + j;
                    bBreak = true;
                }

                if (bBreak) break;
                ArrayList ar3 = new ArrayList();
                ar3.AddRange(ar2);
                ar3.Remove(j);
                foreach (int k in ar3)
                {
                    if (bBreak) break;
                    if ((i + j + k) == iEnd)
                    {
                        s1 = "Results = " + i + ", " + j + ", " + k;
                        bBreak = true;
                    }
                }
            }
        }
        Console.WriteLine(s1);
        bool bBreak = false;
        int iEnd = 13;
        ArrayList ar1 = new ArrayList();
        ar1.Add(2);
        ar1.Add(4);
        ar1.Add(5);
        ar1.Add(7);

        String s1 = " ";
        foreach (int i in ar1)
        {
            if (i == iEnd)
            {
                s1 = "Result = " + i;
                bBreak = true;
            }
            if (bBreak) break;
            ArrayList ar2 = new ArrayList();
            ar2.AddRange(ar1);
            ar2.Remove(i);
            foreach (int j in ar2)
            {
                if ((i + j) == iEnd)
                {
                    s1 = "Results = " + i + ", " + j;
                    bBreak = true;
                }

                if (bBreak) break;
                ArrayList ar3 = new ArrayList();
                ar3.AddRange(ar2);
                ar3.Remove(j);
                foreach (int k in ar3)
                {
                    if (bBreak) break;
                    if ((i + j + k) == iEnd)
                    {
                        s1 = "Results = " + i + ", " + j + ", " + k;
                        bBreak = true;
                    }
                }
            }
        }
        Console.WriteLine(s1);
做个ˇ局外人 2024-08-05 18:54:57

是的,这是可能的。 不确定这个帖子是否还被打开。 但您可能想要使用 Excel Solver 插件。 发布所有数字,相邻单元格上有 1。 然后输入所需的输出数字..然后所有使用的数字将保留其相邻的“1”,而未使用的数字将变为“0”。 然后执行一个过滤公式,仅列出那些旁边有“1”的公式。

Yes this is possible. Not sure if this post is still opened. But you would want to do the Excel Solver add-in. Post all numbers, with 1s on the adjacent cell. Then put the desired output number.. then all the used numbers, will keep their adjacent "1", while the unused ones will turn to "0". Then do a filter formula that lists only those that have a "1" beside it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文