Faster alternative to nested loops?

后端 未结 12 1445
栀梦
栀梦 2020-12-12 18:35

I have a need to create a list of combinations of numbers. The numbers are quite small so I can use byte rather than int. However it requires many

相关标签:
12条回答
  • 2020-12-12 19:10

    List has an array internally where it stores it values, with a fixed length . When You call List.Add it checks if there is enough space. When it dcannot add the new element it will create a new array of a larger size, copy all the previous elements over, then add then new one. This takes quite a few cycles.

    Since you know the number of elements already, you can create the list of the correct size, that should be a lot faster already.

    Also, not sure how you access the values, but you could create this thing one and save the image in code (loading it from disk will probably be slower than what you;re doing now. How many times do you read/ write to this thing?

    0 讨论(0)
  • 2020-12-12 19:10

    Here is another solution. Outside of VS it runs as fast as 437.5 ms which is 26% faster than the original code (593.7 on my computer):

    static List<byte[]> Combinations(byte[] maxs)
    {
      int length = maxs.Length;
      int count = 1; // 3981312;
      Array.ForEach(maxs, m => count *= m);
      byte[][] data = new byte[count][];
      byte[] counters = new byte[length];
    
      for (int r = 0; r < count; r++)
      {
        byte[] row = new byte[length];
        for (int c = 0; c < length; c++)
          row[c] = counters[c];
        data[r] = row;
    
        for (int i = length - 1; i >= 0; i--)
        {
          counters[i]++;
          if (counters[i] == maxs[i])
            counters[i] = 0;
          else
            break;
        }
      }
    
      return data.ToList();
    }
    
    0 讨论(0)
  • 2020-12-12 19:13

    What about using Parallel.For() to run it? (Struct optimization kudos to @Caramiriel). I slightly modified the values (a is 5 instead of 2) so I'm more confident in the results.

        var data = new ConcurrentStack<List<Bytes>>();
        var sw = new Stopwatch();
    
        sw.Start();
    
        Parallel.For(0, 5, () => new List<Bytes>(3*4*3*4*3*3*4*2*4*4*3*4),
          (a, loop, localList) => {
            var bytes = new Bytes();
            bytes.A = (byte) a;
            for (byte b = 0; b < 3; b++) {
              bytes.B = b;
              for (byte c = 0; c < 4; c++) {
                bytes.C = c; 
                for (byte d = 0; d < 3; d++) {
                  bytes.D = d; 
                  for (byte e = 0; e < 4; e++) {
                    bytes.E = e; 
                    for (byte f = 0; f < 3; f++) {
                      bytes.F = f; 
                      for (byte g = 0; g < 3; g++) {
                        bytes.G = g; 
                        for (byte h = 0; h < 4; h++) {
                          bytes.H = h; 
                          for (byte i = 0; i < 2; i++) {
                            bytes.I = i; 
                            for (byte j = 0; j < 4; j++) {
                              bytes.J = j; 
                              for (byte k = 0; k < 4; k++) {
                                bytes.K = k; 
                                for (byte l = 0; l < 3; l++) {
                                  bytes.L = l;
                                  for (byte m = 0; m < 4; m++) {
                                    bytes.M = m;
                                    localList.Add(bytes);
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
    
    
            return localList;
          }, x => {
            data.Push(x);
        });
    
        var joinedData = _join(data);
    

    _join() is a private method, defined as:

    private static IList<Bytes> _join(IEnumerable<IList<Bytes>> data) {
      var value = new List<Bytes>();
      foreach (var d in data) {
        value.AddRange(d);
      }
      return value;
    }
    

    On my system, this version runs approximately 6 times faster (1.718 seconds versus 0.266 seconds).

    0 讨论(0)
  • 2020-12-12 19:19

    All your numbers are compile time constant.

    What about unrolling all the loops into a list (using your program to write code):

    data.Add(new [] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
    data.Add(new [] {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
    etc.
    

    That should at least take away the overhead of the for loops (if there is any).

    I'm not familiar with C# too much, but there seems to be some means of serializing objects. What if you just generated that List and serialized it in some form? I'm not sure if the deserialization is quicker then creating the List and adding the elements, though.

    0 讨论(0)
  • 2020-12-12 19:21

    Here's a different way that only need 2 loop. The idea is to increase the first element and if that number goes over than increase the next one.

    Instead of displaying the data, you can use currentValues.Clone and add that cloned version into your list. For me this ran faster than your version.

    byte[] maxValues = {2, 3, 4};
    byte[] currentValues = {0, 0, 0};
    
    do {
        Console.WriteLine("{0}, {1}, {2}", currentValues[0], currentValues[1], currentValues[2]);
    
        currentValues[0] += 1;
    
        for (int i = 0; i <= maxValues.Count - 2; i++) {
            if (currentValues[i] < maxValues[i]) {
                break;
            }
    
            currentValues[i] = 0;
            currentValues[i + 1] += 1;
        }
    
    // Stop the whole thing if the last number is over
    // } while (currentValues[currentValues.Length-1] < maxValues[maxValues.Length-1]);
    } while (currentValues.Last() < maxValues.Last());
    
    • Hope this code works, I converted it from vb
    0 讨论(0)
  • 2020-12-12 19:21

    Do you need the result to be an array of arrays? With the current setup the length of the inner arrays is fixed and could be replaced with structs. This would allow the entire thing to be reserved as one continuous block of memory and provides easier access to the elements (not sure how you use this thing lateron).

    The approach below is much faster (41ms vs 1071ms for the original on my box):

    struct element {
        public byte a;
        public byte b;
        public byte c;
        public byte d;
        public byte e;
        public byte f;
        public byte g;
        public byte h;
        public byte i;
        public byte j;
        public byte k;
        public byte l;
        public byte m;
    }
    
    element[] WithStruct() {
        var t = new element[3981312];
        int z = 0;
        for (byte a = 0; a < 2; a++)
        for (byte b = 0; b < 3; b++)
        for (byte c = 0; c < 4; c++)
        for (byte d = 0; d < 3; d++)
        for (byte e = 0; e < 4; e++)
        for (byte f = 0; f < 3; f++)
        for (byte g = 0; g < 3; g++)
        for (byte h = 0; h < 4; h++)
        for (byte i = 0; i < 2; i++)
        for (byte j = 0; j < 4; j++)
        for (byte k = 0; k < 4; k++)
        for (byte l = 0; l < 3; l++)
        for (byte m = 0; m < 4; m++)
        {
            t[z].a = a;
            t[z].b = b;
            t[z].c = c;
            t[z].d = d;
            t[z].e = e;
            t[z].f = f;
            t[z].g = g;
            t[z].h = h;
            t[z].i = i;
            t[z].j = j;
            t[z].k = k;
            t[z].l = l;
            t[z].m = m;
            z++;
        }
        return t;
    }
    
    0 讨论(0)
提交回复
热议问题