关于c#:从大小为n的列表中查找哪些数字求和到另一个数字的算法

关于c#:从大小为n的列表中查找哪些数字求和到另一个数字的算法

Algorithm to find which numbers from a list of size n sum to another number

我有一个十进制数字(我们称它为目标)和一个其他十进制数字的数组(我们称其为数组元素),我需要找到从元素求和到目标的所有数字组合。

我偏爱使用C#(.Net 2.0)解决方案,但最好的算法可能会赢得胜利。

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

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

有趣的答案。感谢您提供的指向Wikipedia的指针-虽然很有趣-但实际上并没有解决我在寻找完全匹配项时所说的问题-与传统的装箱/背包问题相比,更多的是会计/账簿平衡问题。

我一直在关注堆栈溢出的开发,并想知道它的用处。这个问题在工作中出现,我想知道堆栈溢出是否可以比我自己编写更快地提供现成的答案(或更好的答案)。也感谢您提出的建议将此作业标记为作业的评论-鉴于上述情况,我认为这是相当准确的。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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++);
            }
        }
    }
}

如果您希望某个应用程序测试其工作原理,请尝试以下控制台应用程序代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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();
    }
}

我希望这可以帮助其他人更快地获得答案(无论是用于家庭作业还是其他)。

干杯...


通过动态编程可以解决子集和问题以及稍微更普遍的背包问题:不需要所有组合的强力枚举。请查阅Wikipedia或您最喜欢的算法参考。

尽管问题是NP完全的,但它们非常"容易" NP完全。元素数量的算法复杂度很低。


我认为您的手上有一个装箱问题(这很难解决NP),所以我认为唯一的解决方案是尝试所有可能的组合,直到找到可行的组合。

编辑:正如评论中指出的那样,您不必总是为遇到的每组数字尝试每种组合。但是,您想出的任何方法都有最坏情况的数字集,您将必须尝试每种组合-或至少有一个组合子集会随该组的大小呈指数增长。

否则,这将不是NP难题。


在不解决暴力破解问题(如其他人已经提到的问题)的同时,您可能希望先对数字进行排序,然后再对剩余的数字进行排序(因为一旦传递了总和值,就不能添加比目标大的数字-和)。

这将改变实现算法的方式(以便仅排序一次,然后跳过标记的元素),但平均而言会提高性能。


您已经描述了背包问题,唯一真正的解决办法是蛮力。有些近似解决方案速度更快,但可能无法满足您的需求。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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);
    }
}


推荐阅读

    linux切换目标命令?

    linux切换目标命令?,系统,密码,工具,命令,一致,工作,用户,终端,目录,用户名,l

    linux中查找替换命令?

    linux中查找替换命令?,标的,文件,名称,代码,内容,字符串,命令,批量,方法,表

    递归查找linux命令?

    递归查找linux命令?,时间,档案,命令,标准,数据,系统,名称,文件,服务,信息,lin

    linux查找设备号命令?

    linux查找设备号命令?,设备,系统,信息,名称,分区,网上,情况,软件,技术,工具,

    linux查找网卡的命令?

    linux查找网卡的命令?,地址,系统,网络,实时,工具,信息,技术指标,电脑,状态,

    linux命令匹配数字?

    linux命令匹配数字?,数字,档案,位置,环境,名字,较大,系统,权限,命令,用户,Lin

    linux权限数字命令?

    linux权限数字命令?,数字,系统,地址,权限,命令,标准,情况,管理,基础,文件,lin

    字符串查找命令linux?

    字符串查找命令linux?,系统,字符串,工具,信息,文件,命令,字符,选项,文本,范

    linux查找帮助的命令?

    linux查找帮助的命令?,系统,命令,信息,软件,名称,文件,指令,进程,表示,参数,l

    linux查找帮助的命令?

    linux查找帮助的命令?,系统,命令,信息,软件,名称,文件,指令,进程,表示,参数,l

    linux查找重复项命令?

    linux查找重复项命令?,工具,系统,电脑,百度,文件,命令,情况,名字,标准,通用,l

    linux命令查找进程?

    linux命令查找进程?,系统,名称,软件,状态,进程,电脑,信息,命令,材料,数据,怎

    linux命令查找日志?

    linux命令查找日志?,地址,信息,系统,名称,对比,状态,实时,命令,日志,等级,lin

    防篡改算法linux命令?

    防篡改算法linux命令?,技术,网络,系统,数据,区块链,电子,交易,信息,网站,国

    linux查找php命令?

    linux查找php命令?,服务,信息,系统,名称,工具,软件,网络,代码,工作,网站,Linu

    linux命令查找内容?

    linux命令查找内容?,命令,文件,网络,名称,信息,工作,标准,系统,管理,位置,lin

    linux数据库查找命令?

    linux数据库查找命令?,位置,名称,状态,服务,软件,信息,系统,命令,名字,密码,

    linux查找包的命令?

    linux查找包的命令?,基础,软件,项目,数据,名称,命令,环境,技术,文件,子目录,

    linux排序数字命令?

    linux排序数字命令?,标准,数字,单位,情况,系统,信息,命令,文件,顺序,参数,lin

    linux命令不能打数字?

    linux命令不能打数字?,工作,密码,数字,地址,电脑,系统,命令,设备,下来,预期,