次の問題にサブキュービックアルゴリズムは存在しますか?


11

n×nA=(aij)

i,j,kmax(aij,aik,ajk)
1i<j<knO(n3)

3
これは、特定のグラフの三角形の数を数えるのと同じくらい難しいことに注意してください。入力行列が "0"がエッジを示し、 "1"が欠落エッジを示すようにグラフをエンコードする場合、が存在する場合に限りノード、、およびによって形成される三角形です。それ以外の場合はです。max(aij,aik,ajk)=0ijk1
Jukka Suomela

1
三角形をカウントするための既知の大幅にサブキュービックなアルゴリズムは、高速行列乗算に基づいていると思いますか?これらの手法をこの問題に適用するのは難しいかもしれません。また、実用的なものを探している場合、高速行列乗算に基づくものは何も役に立ちません。
Jukka Suomela

回答:


3

時間で機能する非常に実用的なアプローチが存在します。ここで、はプロセッサワードのビット数です。主な考え方は、マトリックスの要素を1つずつ昇順で繰り返し(任意にタイを解除)、「スイッチをオンにする」というものです。いくつかのトリプルの最大要素がオンになった瞬間を考えてみます。簡単にするために、上記の要素がであると仮定しましょう。最後の要素がオンになったときに、トリプルの値を答えに追加するのは自然なことです。したがって、とような可能なの数を数える必要がありますO(n3/w)waij,aik,ajkaijkaikajkすでにオンになっています(これはトリプルの数になります。ここではが最大の要素なので、今は完全にオンになっています)。ここでは、ビット最適化を使用することにより、単純な実装を高速化できます。aijO(n)

詳細については、で機能するC ++ 11の次の実装を参照してください (これはあまり最適化されていません。ただし、少なくとも私のマシンでは、単純な合計を大幅に上回っています)。n5000|aij|109n=5000

// code is not very elegant, 
// but should be understandable
// here the matrix a has dimensions n x n
// a has to be symmetric!
int64_t solve (int n, const vector<vector<int32_t>> &a)
{
        std::vector<boost::dynamic_bitset<int64_t>> mat
        (n, boost::dynamic_bitset<int64_t>(n));

        vector<pair<int, int>> order;
        for (int j = 1; j < n; j++)
        for (int i = 0; i < j; i++)
            order.emplace_back(i, j);
        sort(order.begin(), order.end(),
            [&] (const pair<int, int> &l, const pair<int, int> &r) 
            {return a[l.first][l.second] < a[r.first][r.second];});

        int64_t ans = 0;
        for (const auto &position : order)
        {
            int i, j;
            tie (i, j) = position;
            mat[i][j] = mat[j][i] = 1;
            // here it is important that conditions 
            // mat[i][i] = 0 and mat[j][j] = 0 always hold
            ans += (mat[i] & mat[j]).count() * int64_t(a[i][j]);
        }

        return ans;
}

あなたが浮気ビットの最適化を使用することを検討している場合は、得、ここで同じ結果に4つのロシアの方法を使用することができますあまり実用的でなければならないアルゴリズム、(ので最も近代的なハードウェア上でかなり大きいです)しかし理論的にはより優れています。実際、を選択し、行列の各行を整数の配列としてからまで保持します。ここで、番目の数は配列は、から行のビットに対応しO(n3/logn)wblog2nnb02b1iibmin(n,(i+1)b)0-インデックス。時間でそのような2つのブロックごとのスカラー積を事前計算できます。整数を1つだけ変更しているため、行列内の位置の更新は高速です。行とのスカラー積を見つけるには、その行に対応する配列を反復処理するために、対応するブロックのスカラー積をテーブルで検索し、得られた積を合計します。O(22bb)ij

上記の段落では、整数操作に時間かかると仮定しています。アルゴリズムの比較速度を実際には変更しないため、これは非常に一般的な仮定です(たとえば、この仮定を使用しない場合、ブルートフォースメソッドは実際には 時間で機能します(ここではビット演算で時間を測定します)が一定ので少なくともまでの絶対値を持つ整数値を取る 場合(それ以外の場合は、とにかく行列乗算)が4つのロシア方法が使用上記で示唆nO(1)O(n3logn)aijnεε>0O(nε)O(n3/logn)その場合、サイズ数の操作。そのため、ビット演算が行われますが、モデルの変更にもかかわらず、ブルートフォースよりも優れています)。O(logn)O(n3)

、アプローチの存在についての質問はまだ興味深いものです。O(n3ε)

この回答で示されている手法(ビット最適化と4つのロシア語の手法)は決して独創的なものではなく、説明を完全にするためにここに示しています。しかし、それらを適用する方法を見つけることは簡単ではありませんでした。


まず、あなたの提案は実際に役立つと思われますが、私のユースケースで試してみるだけかもしれません。ありがとう!第2に、固定幅の数値型のアルゴリズムの計算の複雑さは依然としてです。アプローチについて詳しく教えてください。私たちはのスカラー積を見つけることができる方法を得ることはありませんし、より速く(私たちはすべての要素にアクセスする場合に必要とされます)。O(n3)O(n3/logn)mat[i]mat[j]O(n)
user89217

また、あなたのコードはmatどちらが重要と思われるかを定義していません。どのように定義できるかは理解して(mat[i] & mat[j]).count()いますが、どのSTLコンテナでも希望どおりに機能するかどうかは疑問です。
user89217

1
についてmat-私は使用する必要があると思いますstd::vector<boost::dynamic_bitset<int64_t>>
user89217

についてmat:はい、実際には標準のビットセットを念頭に置いていましたboost::dynamic_bitsetが、サイズがコンパイル時の定数である必要がないため、この場合はさらに優れています。答えを編集して、この詳細を追加し、4つのロシア語のアプローチを明確にします。
Kaban-5

1
すばらしい、これは私には確かに見えます。マイナーなポイント:トランスディチョモトスモデルでは、マシンワードで操作を実行できると想定しているため、スカラー積を事前に計算する必要はありません。実際、モデルは想定しているので、は少なくともです。そして、あなたが言うように、スカラー積の事前計算は実際には意味がありません(配列のルックアップはバイナリ演算よりも遅くなります)。O(1)wlog2nO(n3/w)O(n3/logn)
user89217
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.