简体   繁体   English

如何优化此代码? (也许使用LINQ?)

[英]How to optimize this code? (Maybe with LINQ?)

I have this code: 我有以下代码:

ArrayList arrayA = new ArrayList();
ArrayList arrayB = new ArrayList();
ArrayList arrayC = new ArrayList();
ArrayList arrayD = new ArrayList();
ArrayList arrayE = new ArrayList();
ArrayList arrayF = new ArrayList();
ArrayList arrayG = new ArrayList();
ArrayList arrayH = new ArrayList();
ArrayList arrayI = new ArrayList();
ArrayList arrayJ = new ArrayList();

int n = 0;

for (decimal a = 0.1m; a <= 100m; a += 0.1m)
{
    for (decimal b = 100m - a; b > 0m; b -= 0.1m)
    {
        for (decimal c = 100m - b; c > 0m; c -= 0.1m)
        {
            for (decimal d = 100m - c; d > 0m; d -= 0.1m)
            {
                for (decimal e = 100m - d; e > 0m; e -= 0.1m)
                {
                    for (decimal f = 100m - e; f > 0m; f -= 0.1m)
                    {
                        for (decimal g = 100m - f; g > 0m; g -= 0.1m)
                        {
                            for (decimal h = 100m - g; h > 0m; h -= 0.1m)
                            {
                                for (decimal i = 100m - h; i > 0m; i -= 0.1m)
                                {
                                    for (decimal j = 100m - i; j > 0m; j -= 0.1m)
                                    {
                                        if ((a + b + c + d + e + f + g + h + i + j) == 100)
                                        {
                                            ++n;
                                            arrayA.Add(a);
                                            arrayB.Add(b);
                                            arrayC.Add(c);
                                            arrayD.Add(d);
                                            arrayE.Add(e);
                                            arrayF.Add(f);
                                            arrayG.Add(g);
                                            arrayH.Add(h);
                                            arrayI.Add(i);
                                            arrayJ.Add(j);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Any ideas how to optimize this? 任何想法如何优化呢? this is taking forever to execute. 这需要永远执行。

Basically what is needed is to know the number of combination for 10 numbers between 0.1 and 100 where the sum of them is exactly 100. (I need to know the number of combinations and the combinations themselves) 基本上,所需的是知道0.1和100之间的10个数字的组合数目,而它们的总和恰好是100。(我需要知道组合数目和组合本身)

Thanks in advance 提前致谢

I do not think there is a straightforward solution for this problem especially with this type of numbers. 我不认为有一个直接解决此问题的方法,尤其是对于这类数字。 But here is a very general idea to help you develop your own algorithm: 但是,这是一个非常笼统的想法,可以帮助您开发自己的算法:

  1. Learn about Gosper's Hack . 了解有关Gosper的Hack的信息 It can generate combinations in O(n) complexity. 它可以生成O(n)复杂度的组合。
  2. You'll realize that you need to generate C(1000,10) combinations, which is not doable directly with primitive data types. 您将认识到需要生成C(1000,10)组合,这不能直接用原始数据类型完成。 To overcome this you can implement your own class that can represent large bit sequences with necessary operations like adding and shifting etc. (Or you can find a similar class in your standard library and extend it.) 为了解决这个问题,您可以实现自己的类,该类可以通过必要的操作(例如加法和移位)来表示大位序列。(或者,您可以在标准库中找到一个类似的类并进行扩展。)

I am aware that this is not an easy task and I would not want to implement this myself. 我知道这不是一件容易的事,我也不想自己实现。 But it will give you an O(n) time complexity, you just iterate over the combinations in linear time, use that bit sequence to pick numbers between 0.1 and 100 with step 0.1 and check if their sum is 100. And add the bit sequence to the final ArrayList. 但这会给您O(n)的时间复杂性,您只需遍历线性时间的组合,使用该位序列在步骤0.1中选择介于0.1和100之间的数字,并检查它们的总和是否为100。并添加位序列到最终的ArrayList。

Edit after Ronan Thibaudau's warning: I am sorry that I jumped into Java directly, but I think you can find the respective approach in your preferred language/framework. 在Ronan Thibaudau发出警告之后进行编辑:很抱歉,我直接跳入Java,但是我认为您可以在自己喜欢的语言/框架中找到相应的方法。

Yet another edit: Now the post is language-independent. 另一个修改:现在,该帖子与语言无关。 Idea is completely same... All I am saying is; 想法是完全一样的。 if you are looking at such a problem with large combinations, this idea can give you O(n) time complexity and O(n) memory requirement. 如果您正在考虑使用大型组合的此类问题,则此想法可以为您提供O(n)的时间复杂度和O(n)的内存需求。 Which virtually guarantees that it will run in a feasible time, consuming feasible amount of memory given any sufficiently large n. 实际上,这保证了它将在可行的时间内运行,在给定足够大的n的情况下会消耗可行的内存量。

Self edit: I am still thinking about it and have to make a correction. 自我编辑:我仍然在考虑这个问题,必须进行更正。 (I am preserving the somewhat wrong algorithmic analysis) (我保留了一些错误的算法分析)

Gosper's Hack will definitely give you O(n) time complexity to generate the combinations, which is essential to have an acceptably optimized implementation. Gosper's Hack肯定会给您O(n)时间复杂度来生成组合,这对于实现可接受的优化实现至关重要。 However, as you will have to create your own class to represent bit sequence, you have to implement your own shifters, adders, ands and whatnot(ie you cannot directly use your hardware for those operations). 但是,由于必须创建自己的类来表示位序列,因此必须实现自己的移位器,加法器,ands和诸如此类的东西(即,您不能直接将硬件用于这些操作)。 For that, you will need another n-loop. 为此,您将需要另一个n循环。 Which would probably make your algorithmic complexity O(n 2 ) 这可能会使您的算法复杂度为O(n 2

It is definitely not as good as O(n), but should still satisfy your execution time demands. 它绝对不如O(n)好,但仍应满足您的执行时间要求。

If you'd really like to optimize proposed algorithm, I'd start by omitting unnecessary loops, it doesn't make sense to continue if the sum is >= 100, eg: 如果您真的想优化建议的算法,那么我将从忽略不必要的循环开始,如果总和> = 100,则继续下去是没有意义的,例如:

for (decimal a = 0.1m; a <= 100m; a += 0.1m)
{
    for (decimal b = 100m - a; b > 0m; b -= 0.1m)
    {
        if (a + b >= 100m)
             continue;

             for (decimal c = 100m - b; c > 0m; c -= 0.1m)
             {
                 if (a + b + c >= 100m)
                     continue;
                 ...

Anyway, you'll be never able to store all combinations. 无论如何,您将永远无法存储所有组合。 Eg, if you have only three numbers, the output would consist of 1000^2 results. 例如,如果只有三个数字,则输出将包含1000 ^ 2个结果。 The first number you can choose arbitrarily (0.1 .. 100, so you have 1000 choices), the same with the second one, eg, I'll choose 0.5 and 42 - the third one I have fixed, 57.5 - so it's 1000 * 1000 * 1 good results. 您可以任意选择第一个数字(0.1 .. 100,因此您有1000个选择),与第二个数字相同,例如,我选择0.5和42-第三个数字我固定为57.5-因此为1000 * 1000 * 1好的结果。 Now extend it on 10 numbers. 现在将其扩展为10个数字。

I've come up with this as the best way to do this with LINQ: 我想出了这是使用LINQ做到这一点的最佳方法:

var n = 1000;
var query =
    from a in Enumerable.Range(1, n)
    from b in Enumerable.Range(1, n - a)
    from c in Enumerable.Range(1, n - a - b)
    from d in Enumerable.Range(1, n - a - b - c)
    from e in Enumerable.Range(1, n - a - b - c - d)
    from f in Enumerable.Range(1, n - a - b - c - d - e)
    from g in Enumerable.Range(1, n - a - b - c - d - e - f)
    from h in Enumerable.Range(1, n - a - b - c - d - e - f - g)
    from i in Enumerable.Range(1, n - a - b - c - d - e - f - g - h)
    let j = n - a - b - c - d - e - f - g - h - i
    where j >= 1
    select new { a, b, c, d, e, f, g, h, i, j };

I've opted to use int and multiply the numbers by 10 rather than using decimal with 0.1 increments. 我选择使用int并将数字乘以10而不是使用带有0.1增量的decimal

Now running this with 1,000 seems to take forever. 现在,用1,000运行此命令似乎要花费很多时间。

So I started running it with smaller numbers and got these results: 所以我开始以较小的数字运行它,并得到以下结果:

10  1
11  10
12  55
13  220
14  715
15  2002
16  5005
17  11440
18  24310
19  48620
20  92378

That's n & the number of combinations returned. 那是n和返回的组合数。

It turns out that this progression is C(n - 1, n - 10) . 事实证明,该级数是C(n - 1, n - 10) So plugging in n = 1000 I get 2,634,095,604,619,700,000,000 combinations. 因此,插入n = 1000可获得2,634,095,604,619,700,000,000组合。

Now, on my computer if I run with n = 30 it takes 11.551 seconds to compute the 10,015,005 combinations. 现在,在我的计算机上,如果我以n = 30运行,则需要11.551秒才能计算出10,015,005个组合。

If you do the maths on that, assuming there are 365.25 days per year, then you come up with the figure that it would take 96,271,110 years to compute for n = 1000 . 如果对此进行数学计算,假设每年有365.25天,那么得出的数字将是需要花费96,271,110年才能计算n = 1000

Even "Deep Thought" was quicker at computing 42. Good luck waiting for it. 甚至“ Deep Thought”在计算42时也更快。祝您好运。

Here's an answer (from 1 to 1000 instead of 0.1 to 100 for simplicity, simply divide all items by 10 to get the value from 0.1 to 100) 这是一个答案(为简单起见,从1到1000而不是0.1到100,只需将所有项目除以10即可得到0.1到100的值)

First off we'll work with generator enumerables and lazy operators to avoid memory issues, everything will get streamed 1 possibility by 1 into the pipeline so memory should be fixed while the code runs 首先,我们将与生成器枚举和惰性运算符一起使用,以避免出现内存问题,一切都会以1的可能性以流方式1流式传输到管道中,因此应在代码运行时固定内存

var kitems = Enumerable.Range(1,1000);
var q = from a in kitems
        from b in kitems
        from c in kitems
        from d in kitems
        from e in kitems
        from f in kitems
        from g in kitems
        from h in kitems
        from i in kitems
        from j in kitems
        where a+b+c+d+e+f+g+h+i+j == 1000
        select new {a,b,c,d,e,f,g,h,i,j};
// Since everything is lazy evaluated, the 10 first results are near immediate (nothing past those is evaluate, this is good for testing, remove the take operator if you want the full dataset)
foreach(var item in q.Take(10))
{
    // Do whatever you want with the result here
}

Let us reason on integer numbers (1 to 1000, sum 1000). 让我们对整数(1到1000,总和1000)进行推理。 Let C(K, N) be the number of combinations involving K variables for a total of N. 令C(K,N)为涉及N个总数的K个变量的组合数。

With a single variable, we have 1 possibility (a=1000), ie C(1, 1000)=1. 对于单个变量,我们有1种可能性(a = 1000),即C(1,1000)= 1。 More generally, C(1, N)=1. 更一般地,C(1,N)= 1。

With two variables, we have 999 combinations (a=1, b=999 to a=999, b=1) and we see that C(2, 1000)= C(1, 999) + C(1, 998) + C(1, 997) + ... C(1, 1). 有两个变量,我们有999个组合(a = 1,b = 999到a = 999,b = 1),我们看到C(2,1000)= C(1,999)+ C(1,998)+ C(1,997)+ ... C(1,1)。 More generally, C(2, N) = N-1. 更一般地,C(2,N)= N-1。

With three variables, we have C(2, 999) combinations with a=1, C(2, 998) with a=2... and C(2, 2) with a=998. 有了三个变量,我们得到a(= 1)的C(2,999),a = 2 ...的C(2,998)和a = 998的C(2,2)。 By the formula of triangular numbers, C(3, N) = N.(N-1)/2. 根据三角数的公式,C(3,N)= N.(N-1)/ 2。

With four variables, we have C(3, 999) combinations with a=1, C(3, 998) with a=2... and C(3, 3) with a=997. 对于四个变量,我们有a(= 1)的C(3,999),a = 2 ...的C(3,998)和a = 997的C(3,3)。 By the formula of tetrahedral numbers C(4, N)= N.(N-1).(N-2) / 6. 通过四面体数的公式C(4,N)= N.(N-1)。(N-2)/ 6。

And so on (this is just Pascal's triangle), until: 依此类推(这只是Pascal的三角形),直到:

C(10, N) = N.(N-1).(N-2).(N-3).(N-4).(N-4).(N-6).(N-7).(N-8).(N-9) / 9! C(10,N)= N.(N-1)。(N-2)。(N-3)。(N-4)。(N-4)。(N-6)。(N-7) 。(N-8)。(N-9)/ 9!

C(10, 1000) = 2634095604619702128324000 C(10,1000)= 2634095604619702128324000

No hope to compute this astronomical number by enumeration ! 没有希望通过枚举来计算这个天文数字!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM