用于查找大小为n的列表中的哪些数字与另一个数字相加的算法

时间:2008-09-17 14:00:40

标签: c# algorithm math np-complete

我有一个十进制数字(让我们称之为目标)和一组其他十进制数字(让我们称之为数组元素)我需要找到所有组合来自元素的数字总和为目标。

我倾向于使用C#(.Net 2.0)中的解决方案,但最好的算法可能无论如何都会获胜。

您的方法签名可能类似于:

public decimal[][] Solve(decimal goal, decimal[] elements)

6 个答案:

答案 0 :(得分:11)

有趣的答案。感谢您对维基百科的指示 - 虽然有趣 - 他们实际上并没有解决问题,因为我正在寻找完全匹配 - 更多的是会计/账面平衡问题,而不是传统的bin-packing /背包问题。

我一直关注堆栈溢出的开发并且想知道它会有多大用处。这个问题出现在工作中,我想知道堆栈溢出是否能够比我自己编写的更快地提供现成的答案(或更好的答案)。还要感谢评论提示这是标记的作业 - 我认为鉴于上述情况,这是相当准确的。

对于那些感兴趣的人,这里是我使用递归的解决方案(当然)我也改变了我对方法签名的看法,然后去了List>而不是decimal [] []作为返回类型:

public class Solver {

    private List<List<decimal>> mResults;

    public List<List<decimal>> Solve(decimal goal, decimal[] elements) {

        mResults = new List<List<decimal>>();
        RecursiveSolve(goal, 0.0m, 
            new List<decimal>(), new List<decimal>(elements), 0);
        return mResults; 
    }

    private void RecursiveSolve(decimal goal, decimal currentSum, 
        List<decimal> included, List<decimal> notIncluded, int startIndex) {

        for (int index = startIndex; index < notIncluded.Count; index++) {

            decimal nextValue = notIncluded[index];
            if (currentSum + nextValue == goal) {
                List<decimal> newResult = new List<decimal>(included);
                newResult.Add(nextValue);
                mResults.Add(newResult);
            }
            else if (currentSum + nextValue < goal) {
                List<decimal> nextIncluded = new List<decimal>(included);
                nextIncluded.Add(nextValue);
                List<decimal> nextNotIncluded = new List<decimal>(notIncluded);
                nextNotIncluded.Remove(nextValue);
                RecursiveSolve(goal, currentSum + nextValue,
                    nextIncluded, nextNotIncluded, startIndex++);
            }
        }
    }
}

如果您希望应用测试此功能,请尝试此控制台应用代码:

class Program {
    static void Main(string[] args) {

        string input;
        decimal goal;
        decimal element;

        do {
            Console.WriteLine("Please enter the goal:");
            input = Console.ReadLine();
        }
        while (!decimal.TryParse(input, out goal));

        Console.WriteLine("Please enter the elements (separated by spaces)");
        input = Console.ReadLine();
        string[] elementsText = input.Split(' ');
        List<decimal> elementsList = new List<decimal>();
        foreach (string elementText in elementsText) {
            if (decimal.TryParse(elementText, out element)) {
                elementsList.Add(element);
            }
        }

        Solver solver = new Solver();
        List<List<decimal>> results = solver.Solve(goal, elementsList.ToArray());
        foreach(List<decimal> result in results) {
            foreach (decimal value in result) {
                Console.Write("{0}\t", value);
            }
            Console.WriteLine();
        }


        Console.ReadLine();
    }
}

我希望这有助于其他人更快地得到答案(无论是作业还是其他)。

干杯...

答案 1 :(得分:3)

我认为你的手上有bin packing problem(这是NP难的),所以我认为唯一的解决办法就是尝试各种可能的组合,直到找到一个有效的方法。

编辑:正如评论中所指出的那样,总是必须为 每个组合尝试每个组合跨越。但是,您提出的任何方法都有最坏情况的数字集合,您 必须尝试每个组合 - 或者至少是增长的组合子集指数与集合的大小。

否则,它不会是NP难的。

答案 2 :(得分:3)

使用动态编程解决了子集和问题以及稍微更普遍的背包问题:不需要对所有组合进行强力枚举。咨询维基百科或您最喜欢的算法参考。

尽管问题是NP完全的,但它们非常“简单”NP完全。元素数量的算法复杂度很低。

答案 3 :(得分:2)

您已经描述过knapsack problem,唯一真正的解决方案就是蛮力。有一些近似解决方案更快,但它们可能不符合您的需求。

答案 4 :(得分:2)

虽然没有解决蛮力的问题(正如其他人已经提到的那样),但您可能需要先对数字进行排序,然后再检查剩下的数字(因为一旦你通过了Sum值,就不能再增加任何数字了比目标 - 总和)。

这将改变你实现算法的方式(为了只排序一次,然后跳过标记的元素),但平均来说会提高性能。

答案 5 :(得分:-1)

public class Logic1 {
    static int val = 121;
    public static void main(String[] args)
    {
        f(new int[] {1,4,5,17,16,100,100}, 0, 0, "{");
    }

    static void f(int[] numbers, int index, int sum, String output)
    {
        System.out.println(output + " } = " + sum);
        //System.out.println("Index value1 is "+index);
        check (sum);
        if (index == numbers.length)
        {
            System.out.println(output + " } = " + sum);
            return;
        }

        // include numbers[index]
        f(numbers, index + 1, sum + numbers[index], output + " " + numbers[index]);
        check (sum);
        //System.out.println("Index value2 is "+index);
        // exclude numbers[index]
        f(numbers, index + 1, sum, output);
        check (sum);
    }

    static void check (int sum1)
    {
        if (sum1 == val)
            System.exit(0);
    }
}