web-dev-qa-db-ja.com

配列内のすべてのペアを生成する時間の複雑さ

数の配列を指定して、すべての一意のペアを生成します。

たとえば、[ 1, 2, 3, 4, 5 ]一意の番号のペアは次のとおりです。

(1, 2), (1, 3), (1, 4), (1, 5)

(2, 3), (2, 4), (2, 5)

(3, 4), (3, 5)

(4, 5)

私の解決策は次のとおりです。

int[] numbers = new int[] { 1, 2, 3, 4, 5 };
HashSet<Pair> pairs = new HashSet<Pair>();

for(int i = 0; i < numbers.Length; i++)
{
    for(int j = i + 1, j < numbers.Length; j++)
    {
        pairs.Add(new Pair(numbers[i], numbers[j]));
    }
}

この時間の複雑さはO(n2 -1)jの繰り返しはiよりも常に1短いため1を減算

この種の問題について少し調査を行ったが、これをより速く行うことができるかどうかについて決定的な答えを見つけることができません。 O(nよりも良い解決策はありますか2 -1)?

21
series0ne

「問題を解決するためのより速い方法」について考える方法の1つは、特定の形式(「おそらく最も解決が難しい/最も難しい」と思われる形式)の出力サイズを調べることです。

出力がO(n^2)の場合、各出力に少なくともO(n^2)を費やす必要があるため、O(1)よりも速く問題を解決できません。

[1, 2, 3, 4, 5]の形式の5つの数字がある場合、一意のペアは

4 pairs in first row
3 pairs in second row
2 pairs...
1 pair

似ているから

(1, 2), (1, 3), (1, 4), (1, 5)

(2, 3), (2, 4), (2, 5)

(3, 4), (3, 5)

(4, 5)

配列に20個の変数([1, 2, 3,... 18, 19, 20]形式)がある場合、次のようになります。

19 pairs
18 pairs
...
2 pairs
1 pair

したがって、出力サイズは(n-1) + (n-2) + (n-3) ... + 3 + 2 + 1です。それを合計する必要があり(シリーズの合計方法を見てください)、結果はO(n^2)です

何が証明されましたか?

最悪のシナリオはAT LEAST O(n^2)です。

また、現時点では、実際の最悪の複雑さはわからないことに注意してください。アルゴリズムはさらに遅くなる場合があります(一部の入力にはO(n^2)が必要であることがわかります)。少なくともこれらのデータにはO(n^2)が必要であることは確かです。異なる入力に対してより速くまたはより遅くすることができます。


結論:アルゴリズムには少なくともO(n^2)時間かかる(最悪の場合のシナリオとして)ことを証明しています。最大O(n^2)時間で実行されるアルゴリズムを作成しました(spyc postで説明)=最適なアルゴリズムがあります。


OPのソリューションへの追加情報:HashSetとの衝突の検出は、「pseudoConstant」のみであり、小さな数字と「運」についてのみです。大量の数字にはO(n)が必要です。そのため、最終的にn^2出力になり、それぞれがnまで処理してn^3の複雑さになります。

タスクを前処理することで解決できます:

1)並べ替え-n log nのみを使用するため、n^2には影響しません

2)2回以上繰り返される番号を削除する[1, 3, 3, 3, 5] -> [1, 3, 3, 5]O(n)

3)次に、このアップデートでアルゴリズムを使用します:

3.1)for iサイクルの開始時:if (number[i] == number[i-1]) continue;

3.2)for jサイクルの開始:最後のペアを覚えておいてください。新しいペアを追加するときは、最後のペアを見て、同じかどうかを確認します。もしそうなら-continue;

例:

Input: [1, 3, 3, 5]

1)i=0, j=1, number[0]=1, number[1]=3 -> add (1, 3)
2)i=0, j=2, number[0]=1, number[2]=3 -> same as last pair, use continue
3)i=0, j=3, number[0]=1, number[3]=5 -> add (1, 5)
4)i=1, j=2, number[1]=3, number[2]=3 -> add (3, 3)
5)i=1, j=3, number[1]=3, number[3]=5 -> add (3, 5)
6)i=2, before go to j-cycle, check number[i] === number[i-1] It is true, use continue
41
libik

次のようになります。

first for loop - O(n)
    second for loop - O(n-1)

最適時間の複雑さ:

enter image description here

  • 1回の反復は無視できますが、最悪のシナリオの時間の複雑さを計算する必要があります enter image description here

特定の文字列の順列の数を取得するために、順列に二項係数を使用することもできます。例えば:

enter image description here

6桁{0,1,2,3,4,5}(n = 6)があり、作成できる置換の数を知りたい場合、つまり:(3,5)、(5,3)など...その後(k = 2、各グループで2桁)、順列の量は次のようになります。

enter image description heredifferent順列、ただしこの場合(3,5)、(5,3)は個別にカウントされるため、それはすべて重要です。 (5,3)と(3,5)を1つcombinationとしてカウントする場合、式は次のようになります。

enter image description here


実装例、順列の計算!

static long factorial(long x) // calcs the factorial TimeCmplx = O(n)
{
    if (x == 1)
        return x;
    return x * factorial(x - 1);
}

static long permutations(long n , long k) //Check that (n , k) >= 0
{            
    // Permutations , n!/(n-k)!
    return factorial(n) / factorial(n - k);
}
12
Yuval

Jの反復は常にiよりも1短いため、この時間の複雑さはO(n2-1)から1を引いたように見えると思います

それが重要な場合(通常、大きなO表記は、最も成長の速い用語のみを記述します)、[i + 1、n]上のjの反復を含む[0、n]上のiの反復があります。反復回数は、n²-1ではなく(n∙(n-1))/ 2です。

また、リストではなくHashSetに変更すると、最悪の場合の実行が変更されますが、 amortised value -ではなく、Pair.GetHashCode()が常に同じ値を返す場合は、それを上げてしまいます。 O(n³)。衝突が一般的なハッシュセットの場合のように、挿入は定数ではなくO(n)になります。

5
Pete Kirkham

これは三角形の面積アルゴリズムです。

[〜#〜] n [〜#〜]入力があり、出力の三角形が必要です。

出力三角形には、N-1高さN-1があります。

_Area of a triangle = height * width / 2
                   = (N-1)  * (N-1) / 2
                   = (N^2 - 2N + 1) / 2
_

O(n^2 - n)は常に最小/最適アルゴリズムコストになります!

0
Oreo