중첩 루프에 대한 더 빠른 대안?


85

숫자 조합 목록을 만들어야합니다. 숫자가 매우 적기 때문에 byte대신 사용할 수 있습니다 int. 그러나 가능한 모든 조합을 얻으려면 많은 중첩 루프가 필요합니다. 내가 추구하는 일을하는 데 더 효율적인 방법이 있는지 궁금합니다. 지금까지 코드는 다음과 같습니다.

var data = new List<byte[]>();
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++)
{
    data.Add(new [] {a, b, c, d, e, f, g, h, i, j, k, l, m});
}

나는 a와 같은 것을 사용하는 것을 고려하고 BitArray있었지만 어떻게 통합 할 수 있을지 모르겠습니다.

어떤 권장 사항이라도 대단히 감사하겠습니다. 또는 이것이 내가 원하는 일을하는 가장 빠른 방법일까요?

몇 가지 빠른 포인트를 편집 하십시오 (원래 게시물에 넣지 않은 사과).

  • 숫자와 그 순서 (2, 3, 4, 3, 4, 3, 3 등)는 매우 중요하므로 LINQ사용하여 순열 생성 과 같은 솔루션을 사용하면 각 '열'의 최대 값이 다음 과 같으므로 도움이되지 않습니다. 다른
  • 나는 수학자가 아니므로 '순열'과 '조합'과 같은 기술 용어를 올바르게 사용하지 않으면 사과드립니다. :)
  • 나는 않습니다 난 그냥 하나를 잡을 수 없거나 다른 인덱스를 기반으로 - 한 번에 이러한 조합을 모두 채울 필요
  • 사용하는 byte것이 사용 하는 것보다 빠르다는 int것을 보증 합니다. 또한 int보다 67m 이상의 바이트 배열을 갖는 것이 메모리 사용량에 훨씬 좋습니다.
  • 내 궁극적 인 목표는 중첩 루프에 대한 더 빠른 대안을 찾는 것입니다.
  • 병렬 프로그래밍 사용을 고려했지만 달성하려는 반복적 특성으로 인해 성공적으로 수행 할 방법을 찾을 수 없었습니다 (를 사용하더라도 ConcurrentBag). 그러나 잘못된 것으로 입증되어 기쁩니다. :)

결론

Caramiriel은 루프에서 약간의 시간을 단축하는 좋은 마이크로 최적화를 제공했기 때문에 그 대답을 올바른 것으로 표시했습니다. Eric은 또한 List를 미리 할당하는 것이 더 빠르다고 언급했습니다. 그러나이 단계에서는 중첩 된 루프가 실제로이를 수행 할 수있는 가장 빠른 방법 인 것 같습니다 (우울 해요, 알아요!).

내가 벤치마킹하려고했던 것을 정확히 시도하고 싶다면 StopWatch각 루프에서 최대 4 개까지 세는 13 개의 루프를 사용하여 목록에서 약 67m + 라인을 만듭니다. 내 컴퓨터 (i5-3320M 2.6GHz)에서 최적화 된 버전을 수행하는 데 약 2.2 초가 걸립니다.


1
LINQ를 사용하려고하면 멀티 코어 프로세서 다음 Parrallel.for 사용하는 경우
Jalpesh Vadgama

1
내가 보는 것을 기반으로 이것이 순열이 아니지만 매우 작은 (2-4 요소) 세트의 조합이 맞습니까 아니면 실제로 세트 의 모든 / 일부 순열을 하십니까?
Carsten

나는 당신이 이미 bing.com/search?q=c%23+permutation+enumerable을 검색 했고 어떤 이유로 (게시물에 언급되지 않음) stackoverflow.com/questions/4319049/ 와 같은 기존 답변에 반대하기로 결정 했다고 가정합니다. ... 목록 고려 이 질문을 개선하기로 결정한 옵션입니다.
Alexei Levenkov 2015

3
이것이 성능에 관한 경우 : 목록 (생성자)을 미리 할당하고 일부 루프를 풀 수 있지만,이 숫자를 미리 계산하고 저장하는 것 외에는 그게 다라고 생각합니다. 루프 (오버 헤드)는 신체 내부에 적은 양의 작업이 있기 때문에 아마도 가장 많은 비용이 듭니다.
Caramiriel

5
@benpage : 모든 조합을 미리 생성해야하는 이유는 무엇입니까? 필요할 때 색인에서 조합을 생성하지 않는 이유는 무엇입니까?
Pieter Witvoet 2015

답변:


61

구조체의 속성을 사용하고 미리 구조체를 할당 할 수 있습니다. 아래 샘플에서 몇 가지 수준을 잘라 냈지만 세부 사항을 파악할 수있을 것이라고 확신합니다. 원래 (릴리스 모드)보다 약 5 ~ 6 배 빠르게 실행됩니다.

블록 :

struct ByteBlock
{
    public byte A;
    public byte B;
    public byte C;
    public byte D;
    public byte E;
}

루프 :

var data = new ByteBlock[2*3*4*3*4];
var counter = 0;

var bytes = new ByteBlock();

for (byte a = 0; a < 2; a++)
{
    bytes.A = 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;
                    data[counter++] = bytes;
                }
            }
        }
    }
}

목록에 추가 할 때마다 새 목록을 할당하지 않기 때문에 더 빠릅니다. 또한이 목록을 생성하므로 다른 모든 값 (a, b, c, d, e)에 대한 참조가 필요합니다. 각 값이 루프 내에서 한 번만 수정되었다고 가정 할 수 있으므로이를 최적화 할 수 있습니다 (데이터 지역성).

또한 부작용에 대한 주석을 읽으십시오.

대신를 사용하도록 답변을 편집 T[]했습니다 List<T>.


1
그것의 구조체, 그래서 당신은 괜찮아 야합니다 =) 그들은 모두 고유합니다. List<T>.Add메서드를 호출 할 때 복사됩니다 .
Caramiriel

4
그것은 더 빨리 당신은 목록 ()에 용량을 할당하는 경우
에릭

5
조심 유래 스택에 너무 많은 개체를 할당 할 때 예외.
Andrei Tătar 2015

7
@Andrew 나는 당신의 요점을 이해하지 못합니다. 이 코드는 재귀 적이 지 않으며 스택 사용량이 최소화됩니다.
CodesInChaos 2015-04-22

3
@Andrew : 그것은 스택 오버플로가 아니라 메모리 부족입니다. 이는 List<T>.Add()방법이 저장할 수있는 범위를 넘어 서기 때문입니다 . 이렇게하면 크기가 조정되고 (크기가 두 배가 됨) 2GB 이상의 메모리가 사용됩니다. 새로운 List <ByteBlock> (maxPerLevel.Aggregate (1, (x, y) => x * y))을 사용하여 사전 할당을 시도하십시오.이 데이터의 전체 2GB 블록이 메모리에 필요하다는 것은 이미 '무작위'입니다. 또한 data.ToArray (); 그 시점에서 항목을 메모리에 두 번 보관하기 때문에 비용이 많이 듭니다. [rephrased]
Caramiriel 2015

33

당신이하고있는 일은 계산입니다 (변수 기수를 사용하지만 여전히 계산 중입니다).

C #을 사용하고 있기 때문에 코드 를 실제로 최적화 할 수있는 유용한 메모리 레이아웃과 데이터 구조를 사용하고 싶지 않다고 가정합니다 .

그래서 여기에 다른 것을 게시하고 있습니다. 귀하의 경우에는 적합하지 않을 수 있지만 주목할 가치가 있습니다. 실제로 목록에 희박한 방식으로 액세스하는 경우 여기에 선형 시간으로 i 번째 요소를 계산할 수있는 클래스가 있습니다. 다른 답변으로 지수보다)

class Counter
{
    public int[] Radices;

    public int[] this[int n]
    {
        get 
        { 
            int[] v = new int[Radices.Length];
            int i = Radices.Length - 1;

            while (n != 0 && i >= 0)
            {
                //Hope C# has an IL-opcode for div-and-reminder like x86 do
                v[i] = n % Radices[i];
                n /= Radices[i--];
            }
            return v;
        }
    }
}

이 클래스를 이런 식으로 사용할 수 있습니다.

Counter c = new Counter();
c.Radices = new int[] { 2,3,4,3,4,3,3,4,2,4,4,3,4};

이제 c[i]목록과 동일합니다. 이름을 l, l[i].

보시다시피 Carry-Ripple 카운터를 간단히 구현할 수 있기 때문에 모든 목록을 미리 계산하더라도 모든 루프를 쉽게 피할 수 있습니다.

카운터는 매우 연구 된 주제입니다. 느낀다면 문헌을 검색 할 것을 강력히 권합니다.


4
나는 당신의 대답을 좋아하지만 다른 모든 대답이 기하 급수적이라고 말하는 것은 사실이 아닙니다.
비스킷

1
Caramiriel의 답변과 비교할 때 속도는 얼마입니까?
John Odom

17
"C- 키디-#", 정말? 그것은 완전히 요구되지 않는 것 같습니다.
KChaloux 2015

2
그리고 그것은 수행합니다 Math.DivRem
Caramiriel

1
어느 정도는 최적화가 사용의 문제라고 생각합니다. 예를 들어, 모든 어레이를 한 번만 사용하는 경우, 제 생각에는 중요한 병목 현상 인 집중적 인 메모리 할당을 피할 수 있습니다. 또한 모든 값을 계산하려면 분할을 피하고 단일 증분 (예 : +1 증분)을 수행한다는 사실을 활용해야합니다. 이것은 "즉시 사용 가능한"answear 또는 프로토 타입으로 더 의도 된 것입니다. 저는 속도를 높이려고하지 않았습니다.이 방식이

14

방법 1

속도를 높이는 한 가지 방법은 이와 같이을 계속 사용하려는 경우 용량을 지정하는 List<byte[]>것입니다.

var data = new List<byte[]>(2 * 3 * 4 * 3 * 4 * 3 * 3 * 4 * 2 * 4 * 4 * 3 * 4);

방법 2

또한 System.Array더 빠른 액세스를 위해 직접 사용할 수 있습니다. 질문이 모든 요소가 메모리에 물리적으로 채워 져야한다고 주장하는 경우이 방법을 권장합니다.

var data = new byte[2 * 3 * 4 * 3 * 4 * 3 * 3 * 4 * 2 * 4 * 4 * 3 * 4][];
int counter = 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++)
                                                    data[counter++] = new[] { a, b, c, d, e, f, g, h, i, j, k, l, m };

이 걸립니다 (596) 에 대한 내 컴퓨터에 완료 밀리 빠른 10.4 % (658ms 소요) 문제의 코드보다.

방법 3

또는 스파 스 방식의 액세스에 적합한 저렴한 초기화를 위해 다음 기술을 사용할 수 있습니다. 이는 일부 요소 만 필요할 수 있고 모든 요소를 ​​미리 결정할 필요가 없다고 간주 될 때 특히 유리합니다. 또한 이와 같은 기술은 메모리가 부족할 때 더 방대한 요소로 작업 할 때 유일하게 실행 가능한 옵션이 될 수 있습니다.

이 구현에서 모든 요소는 액세스시 느리게, 즉석에서 결정됩니다. 당연히 이것은 액세스 중에 발생하는 추가 CPU 비용이 발생합니다.

class HypotheticalBytes
{
    private readonly int _c1, _c2, _c3, _c4, _c5, _c6, _c7, _c8, _c9, _c10, _c11, _c12;
    private readonly int _t0, _t1, _t2, _t3, _t4, _t5, _t6, _t7, _t8, _t9, _t10, _t11;

    public int Count
    {
        get { return _t0; }
    }

    public HypotheticalBytes(
        int c0, int c1, int c2, int c3, int c4, int c5, int c6, int c7, int c8, int c9, int c10, int c11, int c12)
    {
        _c1 = c1;
        _c2 = c2;
        _c3 = c3;
        _c4 = c4;
        _c5 = c5;
        _c6 = c6;
        _c7 = c7;
        _c8 = c8;
        _c9 = c9;
        _c10 = c10;
        _c11 = c11;
        _c12 = c12;
        _t11 = _c12 * c11;
        _t10 = _t11 * c10;
        _t9 = _t10 * c9;
        _t8 = _t9 * c8;
        _t7 = _t8 * c7;
        _t6 = _t7 * c6;
        _t5 = _t6 * c5;
        _t4 = _t5 * c4;
        _t3 = _t4 * c3;
        _t2 = _t3 * c2;
        _t1 = _t2 * c1;
        _t0 = _t1 * c0;
    }

    public byte[] this[int index]
    {
        get
        {
            return new[]
            {
                (byte)(index / _t1),
                (byte)((index / _t2) % _c1),
                (byte)((index / _t3) % _c2),
                (byte)((index / _t4) % _c3),
                (byte)((index / _t5) % _c4),
                (byte)((index / _t6) % _c5),
                (byte)((index / _t7) % _c6),
                (byte)((index / _t8) % _c7),
                (byte)((index / _t9) % _c8),
                (byte)((index / _t10) % _c9),
                (byte)((index / _t11) % _c10),
                (byte)((index / _c12) % _c11),
                (byte)(index % _c12)
            };
        }
    }
}

이 걸리는 897 내 컴퓨터에 완료하는 데 밀리 (또한 생성 및에 추가 Array같이 방법 2 ) 약이다, 느린 36.3 % (658ms 소요) 문제의 코드보다.


1
두 번째 제안은 또한 메모리 소비 측면에서 상당한 절약입니다. (그러나 나는 그것이 목록이 변경되지해야한다고 가정합니다 것)
Taemyr

한 번에 전체 목록을 만들어야합니다. 목록 내에서 색인을 참조 할 수 없습니다.
benpage apr

@Taemyr 감사합니다. 그에 따라 업데이트하겠습니다. 구현시 전체 목록이 미리 채워져 있다고 진정으로 주장하는 경우이 세 번째 옵션은 분명히 작동하지 않을 것입니다.
비스킷

3
@benpage 왜 목록을 채워야합니까?
Taemyr

14

내 컴퓨터에서 이것은 222ms 대 760ms (13 for 루프)의 조합을 생성합니다.

private static byte[,] GenerateCombinations(byte[] maxNumberPerLevel)
{
    var levels = maxNumberPerLevel.Length;

    var periodsPerLevel = new int[levels];
    var totalItems = 1;
    for (var i = 0; i < levels; i++)
    {
        periodsPerLevel[i] = totalItems;
        totalItems *= maxNumberPerLevel[i];
    }

    var results = new byte[totalItems, levels];

    Parallel.For(0, levels, level =>
    {
        var periodPerLevel = periodsPerLevel[level];
        var maxPerLevel = maxNumberPerLevel[level];
        for (var i = 0; i < totalItems; i++)
            results[i, level] = (byte)(i / periodPerLevel % maxPerLevel);
    });

    return results;
}

이것은 훌륭한 대답입니다! 불행히도 중첩 루프보다 느리게 실행됩니다. TPL을 사용하여 편집 할 수 있습니까?
benpage

안타깝게도 여전히 상당히 느립니다.
benpage apr

1
@benpage 적어도 2 배 더 빠르게 만드는 쉬운 방법이 있습니다. 결과 유형을 int [,]로 변경하기 만하면됩니다. 이것은 한 번의 호출로 전체 어레이 메모리를 할당합니다. 그것이 귀하의 요구에 어떻게 부합하는지 모르겠습니다 (반환 유형 변경).
안드레이 타타르

8
var numbers = new[] { 2, 3, 4, 3, 4, 3, 3, 4, 2, 4, 4, 3, 4 };
var result = (numbers.Select(i => Enumerable.Range(0, i))).CartesianProduct();

http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/ 에서 확장 방법 사용

public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
    // base case: 
    IEnumerable<IEnumerable<T>> result =
        new[] { Enumerable.Empty<T>() };
    foreach (var sequence in sequences)
    {
        // don't close over the loop variable (fixed in C# 5 BTW)
        var s = sequence;
        // recursive case: use SelectMany to build 
        // the new product out of the old one 
        result =
            from seq in result
            from item in s
            select seq.Concat(new[] { item });
    }
    return result;
}

1
이것은 훨씬 더 느리게 실행됩니다 :(
benpage

8

List에는 내부적으로 배열이 있으며 고정 길이로 값을 저장합니다. List.Add를 호출하면 충분한 공간이 있는지 확인합니다. 새 요소를 추가 할 수 없으면 더 큰 크기의 새 배열을 만들고 이전 요소를 모두 복사 한 다음 새 요소를 추가합니다. 이것은 꽤 많은 사이클이 걸립니다.

이미 요소 수를 알고 있으므로 올바른 크기의 목록을 만들 수 있습니다. 이미 훨씬 더 빨라질 것입니다.

또한 값에 액세스하는 방법은 확실하지 않지만이 항목을 만들고 코드에 이미지를 저장할 수 있습니다 (디스크에서로드하는 것이 지금하는 것보다 느릴 것입니다. 맡은 일?


나는 실제로 일반 배열을 사전 할당하려고 시도했으며 믿거 나 말거나 느립니다. 위에서 말했듯이, 이것은 즉석에서 만들어야하므로 한 번 계산할 수 없으며 남겨 둘 수 없습니다.
benpage

정말? 와우-최적화가 활성화 된 상태에서 실행하고 있습니까? (그냥 묻는다)
Carsten

아 그게 또 다른 문제입니다. 일반 배열 [x, y]는 사용하기 좋지만 배열 배열이 더 빠릅니다. stackoverflow.com/questions/597720/... 때문에 그들이 IL에 후드 implemeted있는 방법의
gjvdkamp

5

2 개의 루프 만 필요한 다른 방법이 있습니다. 아이디어는 첫 번째 요소를 늘리고 그 숫자가 다음 요소를 늘리는 것보다 넘어가는 것입니다.

데이터를 표시하는 대신 currentValues.Clone을 사용하고 복제 된 버전을 목록에 추가 할 수 있습니다. 나를 위해 이것은 귀하의 버전보다 빠르게 실행되었습니다.

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());
  • 이 코드가 작동하기를 바랍니다. vb에서 변환했습니다.

3

모든 숫자는 컴파일 시간 상수입니다.

모든 루프를 목록으로 풀면 (프로그램을 사용하여 코드 작성) :

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.

최소한 for 루프의 오버 헤드를 제거해야합니다 (있는 경우).

나는 C #에 너무 익숙하지 않지만 객체를 직렬화하는 수단이있는 것 같습니다. 그 목록을 방금 생성하고 어떤 형태로 직렬화했다면 어떨까요? 하지만 역 직렬화가 목록을 만들고 요소를 추가하는 것보다 더 빠른지 확실하지 않습니다.


직렬화는 상자 접근 방식을 벗어난 정말 훌륭한 생각입니다!
Joel B

불행히도 목록의 최대 값은 동적이므로 이것을 정적으로 입력 할 수 없습니다. 그래도 좋은 생각입니다!
benpage

2

결과가 배열의 배열이어야합니까? 현재 설정에서는 내부 배열의 길이가 고정되고 구조체로 대체 될 수 있습니다. 이렇게하면 전체를 하나의 연속적인 메모리 블록으로 예약 할 수 있고 요소에 더 쉽게 액세스 할 수 있습니다 (나중에이 작업을 어떻게 사용하는지 확실하지 않음).

아래의 접근 방식은 훨씬 빠릅니다 (내 상자에있는 원본의 경우 41ms 대 1071ms).

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;
}

좋은 생각입니다. 사실, 이것이 제가 실제 프로젝트에서 한 일입니다. 단순성 때문에 원래 솔루션에 넣지 않았습니다. 주로 중첩 루프에 대한 더 나은 대안을 찾고있었습니다.
benpage

1

Parallel.For()그것을 실행하는 데 사용 하는 것은 어떻습니까? ( @Caramiriel에 대한 구조 최적화 찬사 ). 값을 약간 수정하여 (a는 2가 아닌 5) 결과에 더 확신합니다.

    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() 다음과 같이 정의되는 private 메서드입니다.

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

내 시스템에서이 버전은 약 6 배 더 빠르게 실행됩니다 (1.718 초 대 0.266 초).


1
이것은 당신에게 거짓 공유를 제공하는 것을 거의 보장하며 아마도 몇 배 더 느릴 것입니다.
gjvdkamp

나쁘지 않습니다. 불행히도 for 루프보다 느리게 실행됩니다 . FWIW 나는 모든 Parallel.Fors 및 VS 충돌로 시도 했습니다!
benpage

@gjvdkamp 나는 잘못된 공유 문제를 제거한다고 믿는 병렬 버전으로 내 대답을 업데이트했습니다.
jdphenix 2015

0

일부 숫자는 정수 비트 수에 전적으로 적합하므로 상위 레벨 숫자로 "포장"할 수 있습니다.

for (byte lm = 0; lm < 12; lm++)
{
    ...
    t[z].l = (lm&12)>>2;
    t[z].m = lm&3;
    ...
}

물론 이것은 코드의 가독성을 떨어 뜨리지 만 하나의 루프를 저장했습니다. 이것은 숫자 중 하나가 2의 거듭 제곱 일 때마다 수행 할 수 있으며, 이는 귀하의 경우 7 번입니다.


이 답변에 대해 더 알고 싶습니다. 확장 할 수 있습니까?
benpage

답변이 늦어서 죄송합니다. m은 0에서 3으로, 이진수로 00에서 11로, l은 0에서 2로, 00에서 10으로 만들어 지므로 따로 인쇄하면 다음과 같이됩니다. 00 00 00 01 00 10 00 11 01 00 .. . 10 11 0000에서 1011까지 4 비트의 단일 수로 이들을 함께 mege 할 수 있으며 마스크를 사용하여 적절한 비트를 선택할 수 있습니다. lm & 3은 양방향을 만들고 lm과 (11) b lm & 12는 lm과 동일하게 만듭니다. 그리고 (1100) b 그런 다음 "실수"숫자를 갖도록 2 비트만큼 이동합니다. 그건 그렇고,이 경우에 lm >> 2를하는 것이 좋다는 것을 깨달았습니다.
Fabien Dupont

0

여기 또 다른 해결책이 있습니다. VS 외부에서는 원래 코드 (내 컴퓨터의 593.7)보다 26 % 더 빠른 437.5ms만큼 빠르게 실행됩니다.

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();
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.