私はプログラマーであり、アルゴリズムの読み始めたばかりです。私は、Bog Oh、Big Omega、Big Thetaという表記に完全には確信していません。その理由はBig Ohの定義によるものであり、関数g(x)が常にf(x)以上になるようにする必要がある)またはf(x) <= cn n> n0のすべての値。
定義で定数値について言及しないのはなぜですか?たとえば、関数6n + 4としましょう。これをO(n)と表します。しかし、この定義がすべての定数値に当てはまることは真実ではありません。これは、c> = 10およびn> = 1の場合にのみ有効です。cの値が6よりも小さい場合、n0の値は増加します。それでは、定数の値を定義の一部として言及しないのはなぜですか?
いくつかの理由がありますが、おそらく最も重要な理由は、定数がアルゴリズム自体ではなく、アルゴリズムの実装の関数であることです。アルゴリズムの順序は、実装に関係なくアルゴリズムを比較するのに役立ちます。
quicksort の実際のランタイムは、CまたはPythonまたはScalaまたはPostscriptで実装されている場合、通常は変更されます。同じことが当てはまります。 バブルソート -ランタイムは実装に応じて大きく異なります。
ただし、データセットが大きくなると、バブルソートを実行するのに必要な時間が長くなる高速になるという点は変わりません。通常のケースでクイックソートを実行するのに必要な時間よりも、実装に適した言語またはマシンであっても、合理的に正しい実装を前提としています。この単純な事実により、具体的な詳細が利用できない場合に、アルゴリズム自体についてインテリジェントな推論を行うことができます。
アルゴリズムのorderは、実際の測定では重要ですが、抽象的にアルゴリズムを比較するときにノイズになりがちな要因を除外します。
O(n)およびその他の順序表記は、(通常)小さな値に対する関数の動作には関係しません。これは、非常に大きな値、つまりnが無限大に向かって移動するときの制限に対する関数の動作に関係しています。
定数は技術的に重要ですが、nが十分に大きくなると、cの値は完全に無関係になるので、定数は通常抽象化されます。 cの値が重要な場合は、分析に含めることができますが、比較される関数に非常に大きな定数因子がない場合、または効率が特に重要な問題でない限り、通常はそうではありません。
定義によるビッグO表記は次のように述べています。
_For a given function g(n), we denote by O(g(n)) the set of functions:
_
O(g(n)) = {f(n): there exist positive constants c and n' such that 0<=f(n)<=c.g(n) for all n > n'}
Big O表記は、n 'の右側にあるすべての値について、f(n)の値がcg( n)。
これらの定数は単なる定数であり、それらと同じくらい大きくなる可能性のある可変量ではないため、高い値の(変数)係数(n-squareやn-cubeなど)に移動する場合も関係ありません。要因。
以下に示すのは、Big-O表記のグラフです。
この表記法の本質は、「how lower is f(n) from c.g(n) and not when it starts becoming lower
」という事実にあります。
アルゴリズム分析では、Order of Growthが重要な抽象概念であり、入力サイズの変化に応じて実行時間が変化する割合を示します。アルゴリズムに実行時間f(n) = 2n + 3
があるとします。次に、いくつかの入力サイズを接続します。
n = 10: 2 * 10 + 3 = 23
n = 100: 2 * 100 + 3 = 203
n = 10000: 2 * 10000 + 3 = 20003
n = 1000000: 2 * 1000000 + 3 = 2000003
n = 100000000 : 2 * 100000000 + 3 = 200000003
ご覧のとおり、成長の順序は主に変数n
によって決定されます。定数2と3はそれほど重要ではなく、入力サイズが大きくなると、それを決定する上で重要度が低くなります。これが、アルゴリズム分析において、定数が関数の成長の順序を決定する変数に有利になっている理由です。
6n + 4
のパフォーマンス関数がある場合、関連する質問は「6何?」です。 1つのコメントが尋ねたように:あなたの定数は何を表していますか?物理学の用語では、定数係数の単位は何ですか?
O()表記がアルゴリズムのパフォーマンスを説明するために非常に広く使用されている理由は、その質問に答えるための移植可能な方法がないためです。プロセッサによって、クロックサイクルの数と量が異なります同じ基本計算を実行する時間、または関連する基本計算が異なる場合があります。異なるコンピューター言語、または疑似コードのような異なる公式および非公式の記述は、直接比較するのが難しい方法でアルゴリズムを表します。同じ言語での実装でさえ、同じアルゴリズムを異なる方法で表現します-行数のような簡単なフォーマットの詳細はさておき、一般に、特定のアルゴリズムを実装するためのさまざまな任意の構造上の選択肢があります。
別の見方をすると、「アルゴリズム」を使用して特定の実装を説明するのではなく、同じ一般的な手順の潜在的な実装のクラス全体を説明します。この抽象化は、一般的な価値のあるものを文書化するために実装の詳細を無視し、一定のパフォーマンス要因はこれらの詳細の1つです。
とはいえ、アルゴリズムの説明には、実際のハードウェアでの実際の実装のパフォーマンスを説明する民間伝承、メモ、または実際のベンチマークが伴うことがよくあります。これにより、どのような定数要素が期待できるかがおおよそわかりますが、実際のパフォーマンスは、特定の実装の最適化に費やされた作業量などに依存するため、細かいことも考慮する必要があります。また、長期的には、最新かつ最高のプロセッサーのアーキテクチャーが変化するにつれて、比較可能なアルゴリズムの相対的なパフォーマンスはドリフトする傾向があります...
Big-Oh表記の全体的な概念は、特に定数を無視し、アルゴリズムのランタイムを記述する関数の最も重要な部分を提示することです。
少しの間、正式な定義を忘れてください。 _n^2 - 5000
_と_5000 n + 60000
_のどちらが悪い(成長が速い)関数ですか。 n
が約5000未満の場合、線形関数は大きくなります(したがって悪くなります)。それを超えると(正確な値5013?)、2次方程式は大きくなります。
正の数は5000を超える数(かなり多い数)が多いので、2次式を一般に「より大きい」(悪い)関数と見なします。順序表記(Big-Ohなど)はそれを強制します(これらの定義を使用して、常に加法定数と乗法定数を削除できます)。
もちろん、物事は必ずしも単純ではありません。 doでこれらの定数を知りたい場合があります。挿入ソートとバブルソートのどちらが優れていますか?どちらもO(n^2)
です。しかし、一方が他方より本当に優れています。より複雑な分析を行うと、疑問に思っているような定数を取得できます。 Big-Oh関数の計算は、より正確な関数よりもはるかに簡単です。
Big-Ohは、これらの定数を無視して、最も重要な比較を簡素化および容易にします。通常はしないでください(ほとんど関係のない)定数について知りたいので、この表記が気に入っています。
(これはより長い答えなので、要約の太字を読みます)
例を見てみましょう、それをステップバイステップで実行し、私たちがしていることの背後にある目的を理解してください。私たちはあなたの機能とそのビッグオー表記を見つけるという目標から始めます:
_f(n) = 6n+4
_
まず、let O(g(n))
をf(n)
で検索しようとしているBig Oh表記にします。 Big Ohの定義から、simplifiedg(n)
を見つける必要がありますc
and _n0
_ where c*g(n) >= f(n)
is true for all n
's than _n0
_。
まず、g(n) = 6n + 4
を選択しましょう(Big OhではO(6n+4)
になります)。この場合、g(n)
は常にf(n)
と等しいため、_c = 1
_および_n0
_の値は、Big Ohの定義からの数学的要件を満たすことがわかります。
_c*g(n) >= f(n)
1*(6n + 4) >= 6n + 4 //True for all n's, so we don't need to pick an n0
_
この時点で、数学的要件を満たしています。 O(6n+4)
で停止した場合、これがf(n)
を書くことよりも役に立たないことは明らかなので、ビッグオー表記法の真の目的を見逃してしまいます。アルゴリズムの一般的な時間の複雑さを理解することです!したがって、次のステップである単純化に進みましょう。
まず、_6n
_を単純化して、Big [_]をO(4)
にすることができますか?いいえ!(理由が理解できない読者のための練習)
次に、_4
_を簡略化して、Big OhがO(6n)
になるようにしますか?はい!その場合、g(n) = 6n
なので、
_c*g(n) >= f(n)
c*6n >= 6n + 4
_
この時点で_c = 2
_を選択してみましょう。n
の増分ごとに、左側(12)が右側(6)より速く増加します。
_2*6n >= 6n + 4
_
ここで、正の_n0
_を見つける必要があります。ここで、上記の方程式はすべてのn
がその値より大きい場合に当てはまります。左側が右側より速く増加していることはすでにわかっているので、必要なのは1つの正の解決策を見つけることだけです。したがって、_n0 = 2
_は上記を真にするため、g(n)=6n
、またはO(6n)
がf(n)
のBig Oh表記になる可能性があることがわかります。
さて、_6
_を簡略化して、Big OhがO(n)
になるようにできますか?はい!その場合、g(n) = n
なので、
_c*g(n) >= f(n)
c*n >= 6n + 4
_
左が右より速く増加するため、_c = 7
_を選択しましょう。
_7*n >= 6n + 4
_
上記は_n0 = 4
_以上のすべてのn
に当てはまることがわかります。したがって、O(n)
はf(n)
の潜在的なビッグオー表記法です。 g(n)
を簡略化できますか?違う!
最後に、_f(n)
の最も単純なBig Oh表記はO(n)
であることがわかりました。なぜこれをすべて行ったのですか? これで、f(n)
は線形であることがわかりました。これは、ビッグオー表記法が線形複雑度O(n)
であるためです。 これで、f(n)
の時間の複雑さを他のアルゴリズムと比較できるようになりました!たとえば、f(n)
は、関数h(n) = 123n + 72
、i(n) = n
、j(n) = .0002n + 1234
などと同等の時間の複雑さです。上で概説した同じ簡略化プロセスを使用するため、これらはすべてO(n)
の線形時間複雑性を持っています。
甘い!!!