1. ホーム
  2. c#

[解決済み] ネストされたループに代わるより高速な方法?

2023-05-23 22:45:31

質問

数字の組み合わせのリストを作成する必要があります。数字は非常に小さいので、私は使用することができます 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});
}

のようなものを使うことを検討していました。 BitArray のようなものを使おうと思ったのですが、どのように組み込めばいいのかわかりません。

何か推奨事項があれば、非常にありがたいです。あるいは、おそらくこれが私が望むことを行う最速の方法でしょうか?

EDIT いくつかの簡単なポイント(そして、私は元の投稿にこれらを入れていないことを申し訳ありません)。

  • 数字とその順序 (2, 3, 4, 3, 4, 3, 3 など) は非常に重要なので、次のようなソリューションを使用します。 LINQを使用した順列の生成 のようなソリューションを使用しても、各「列」の最大値が異なるため、役に立ちません。
  • 私は数学者ではないので、「並べ換え」や「組み合わせ」といった専門用語を正しく使っていなければ申し訳ありません :)
  • I する は、これらの組み合わせのすべてを一度に入力する必要があります - インデックスに基づいて、1 つまたは別のものを取得することはできません。
  • 使用方法 byte を使用する方が int , I 保証 です。また、67m以上の配列はint型よりもbyte型の方がメモリ使用量に優れています。
  • ここでの私の最終的な目標は、ネストされたループのより速い代替品を探すことです。
  • 並列プログラミングを使うことも考えましたが、私が達成しようとしていることの反復的な性質のために、うまくやる方法を見つけることができませんでした(たとえ ConcurrentBag を使っても) うまくいく方法が見つかりませんでした。)

結論

Caramirielがループの時間を短縮する良いマイクロ最適化を提供してくれたので、その答えを正解としてマークしました。Eric も、List を事前に割り当てる方が高速であると述べています。しかし、現段階では、ネストされたループが実際にこれを行う最速の方法であるようです(気が滅入りますね!)。

で私がベンチマークしようとしていたことを正確に試したい場合は、次のようになります。 StopWatch を使用する場合、各ループで 4 つまでカウントする 13 ループで行ってください - リストの行数は約 67m 以上になります。私のマシン (i5-3320M 2.6GHz) では、最適化されたバージョンを実行するのに、約 2.2 秒かかりました。

どのように解決するのですか?

注意:あなた自身のソリューションを開発している間は、おそらくこの種のコードは必要ないでしょう。これは非常に特殊な状況でのみ使用できますし、使用すべきです。読みやすさはしばしば速度よりも重要です。

構造体のプロパティを利用し、あらかじめ構造体を割り当てることができます。下のサンプルでは一部レベルを切っていますが、具体的な内容はおわかりになると思います。オリジナル(リリースモード)より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> .