PCでプログラムを実行する場合のBig O分析の特定の側面を理解しようとしています。
O(n + 2)のパフォーマンスを持つアルゴリズムがあるとします。ここでnが本当に大きくなると、2は重要ではなくなります。この場合、実際のパフォーマンスがO(n)であることは完全に明らかです。
ただし、別のアルゴリズムの平均パフォーマンスがO(n2 / 2)。この例を見た本には、実際のパフォーマンスはO(n2)。理由がわからないのですが、この場合の2はまったく重要ではないと思われます。それで、私は本からニースの明確な説明を探していました。本はそれをこのように説明しています:
「1/2の意味を考えてみてください。各値をチェックする実際の時間は、コードが変換する機械語命令と、CPUが命令を実行できる速度に大きく依存します。したがって、1/2はtは非常に意味します。」
そして私の反応は...ハァッ?私は文字通りそれが何を言っているのか、より正確にはその発言が彼らの結論と何をしているのか手掛かりがありません。誰かが私に代わってそれを綴ってくれませんか。
助けてくれてありがとう。
「これらの定数は意味があるのか、関連があるのか」には違いがあります。そして「ビッグオー記法はそれらを気にしていますか?」その2番目の質問への答えは「いいえ」ですが、最初の質問への答えは「絶対に!」です。
Big-O表記は、絶対的な大きさではなく、関数の長期的な成長率を表すだけなので、定数については気にしません。関数に定数を乗算しても、一定の量だけ成長率に影響するため、線形関数は引き続き線形に成長し、対数関数は依然として対数的に成長し、指数関数は依然として指数関数的に成長します。これらのカテゴリは定数の影響を受けないため、定数を削除することも重要です。
つまり、これらの定数は絶対に有意です!ランタイムが10の関数100nはwayになります。実行時間がnの関数よりも遅くなります。ランタイムがnの関数2 / 2は、実行時間がnである関数よりも高速になります。2。最初の2つの関数は両方ともO(n)であり、次の2つはO(n2)は、それらが同じ時間内に実行されないという事実を変更しません。それは、big-O表記が設計されているものではないためです。 長期的にある関数が別の関数よりも大きくなるかどうかを判断するには、O表記が適しています。 10でも100nはn> 0の巨大な値であり、その関数はO(n)であるため、nが十分に大きい場合、最終的にはランタイムがnである関数に勝ります。2 / 2その関数はO(n2)。
要約すると、big-Oは成長率の相対的なクラスについてのみ話しているため、定数係数は無視されます。ただし、これらの定数は絶対的に重要です。それらは、漸近分析には関係ありません。
お役に立てれば!
Big-O表記は、一部のマシンでのアルゴリズムの実際の実行時間ではなく、数学関数の観点からアルゴリズムの成長率を記述するだけです。
数学的には、f(x)とg(x)をxに対して十分に大きい正の値にします。 f(x)とg(x)は、xが無限大になる傾向と同じ速度で成長する場合、
ここで、f(x)= x ^ 2およびg(x)= x ^ 2/2とし、次にlim(x-> infinity)f(x)/ g(x)= 2とします。したがって、x ^ 2とx ^ 2/2はどちらも同じ成長率を持っています。したがって、O(x ^ 2/2)= O(x ^ 2)と言えます。
Templatetypedefが言ったように、漸近表記の隠された定数は絶対に重要です。例として、:margeソートはO(nlogn)最悪の場合の時間で実行され、挿入ソートはO(n ^ 2)の最悪の場合の時間で実行されます。ただし、挿入ソートの隠された定数係数はマージソートのそれよりも小さいため、実際には、挿入ソートはマージ問題よりも高速で、多くのマシンで問題のサイズが小さくなります。
定数が重要であることは完全に正しいです。同じ問題に対して多くの異なるアルゴリズムを比較する場合、定数のないO数は、それらが互いに比較する方法の概要を提供します。次に、同じOクラスに2つのアルゴリズムがある場合、関連する定数を使用してそれらを比較します。
しかし、異なるOクラスの場合でも、定数は重要です。たとえば、多桁または大きな整数の乗算の場合、ナイーブアルゴリズムはO(n ^ 2)、カラツバはO(n ^ log_2(3))、Toom-Cook O(n ^ log_3(5))およびSchönhage-StrassenOです。 (n * log(n)* log(log(n)))。ただし、より高速な各アルゴリズムでは、大きな定数に反映されるオーバーヘッドがますます大きくなります。したがって、おおよそのクロスオーバーポイントを取得するには、これらの定数の有効な推定が必要です。したがって、Swagのように、n = 16までは単純な乗算が最も速く、n = 50はカラツバであり、Toom-CookからSchönhage-Strassenへのクロスオーバーはn = 200で発生します。
実際には、クロスオーバーポイントは定数に依存するだけでなく、プロセッサキャッシュやその他のハードウェア関連の問題にも依存します。
Big O表記は、アルゴリズムの実行時間を記述するために最も一般的に使用されます。この文脈では、特定の定数値は本質的に無意味であると私は主張します。次の会話を想像してみてください。
アリス:あなたのアルゴリズムの実行時間は何ですか?
ボブ:7n2
アリス:7nとはどういう意味ですか2?
ご覧のとおり、特定の定数値を示す試みは本質的に問題があります。しかし、一定の要因を取り除いたら、アルゴリズムの実行時間を明確に説明できます。 Big O表記は、アルゴリズムの実装と実行の技術的特徴から抽象化しながら、アルゴリズムにかかる時間の堅牢で有用な説明を提供します。
これで、(適切に定義された)操作の数やアルゴリズムが実行するCPU命令、並べ替えアルゴリズムが実行する比較の数などを記述するときに定数係数を指定できるようになりました。しかし、通常、私たちが本当に関心を持っているのは実行時間です。
これは、アルゴリズムの実際のパフォーマンス特性が重要でないことを示唆するものではありません。たとえば、行列乗算のアルゴリズムが必要な場合、Coppersmith-Winogradアルゴリズムはお勧めできません。このアルゴリズムがO(n2.376)時間、最強のライバルであるStrassenアルゴリズムはO(n2.808)時間。ただし、ウィキペディアによると、Coppersmith-Winogradは実際には遅いため、「最新のハードウェアでは処理できないほど大きなマトリックスに対してのみ利点があります」。これは通常、Coppersmith-Winogradの定数が非常に大きいことで説明されます。ただし、繰り返しますが、Coppersmith-Winogradの実行時間について話している場合、定数係数に特定の数値を指定しても意味がありません。
その制限にもかかわらず、ビッグO表記は実行時間のかなり良い尺度です。そして多くの場合、1行のコードを書く前に、十分に大きな入力サイズに対してどのアルゴリズムが最も高速であるかがわかります。
定数のないビッグOは、アルゴリズム分析には十分です。
まず、実際の時間は、命令の数だけでなく、コードが実行されるプラットフォームに密接に関連している各命令の時間にも依存します。それは理論分析以上のものです。したがって、ほとんどの場合、定数は必要ありません。
次に、Big Oは主に、問題が大きくなるにつれてランタイムがどのように増加するか、またはハードウェアのパフォーマンスが向上するにつれてランタイムがどのように減少するかを測定するために使用されます。
第3に、パフォーマンスが最適化されている状況では、定数も考慮されます。
コンピューターで特定のタスクを実行するのに必要な時間は、入力された値が非常に大きい場合を除いて、今ではそれほど多くの時間を必要としません。
サイズ10 * 10の2つの行列を乗算したい場合、問題は発生しませんnlessこの演算を実行したいmultiple timesそして、-asymptotic表記法が普及しますそしてnの値が非常に大きくなると、定数は実際には答えに影響を与えず、ほとんど無視できますしたがって、複雑さを計算する間、それらを残す傾向があります。
O(n+n)
の時間の複雑さはO(2n)
に減少します。現在、2
は定数です。したがって、時間の複雑さは基本的にn
に依存します。
したがって、O(2n)
の時間の複雑さはO(n)
と等しくなります。
また、このようなものがある場合でも、O(2n + 3)
は、本質的には時間はnのサイズに依存するため、引き続きO(n)
になります。
ここで、O(n^2 + n)
のコードがあるとすると、nの値が増加すると、nの影響がn^2
の影響と比較して重要性が低くなるため、O(n^2)
になります。
例えば:
n = 2 => 4 + 2 = 6
n = 100 => 10000 + 100 => 10100
n = 10000 => 100000000 + 10000 => 100010000
ご覧のように、2番目の式の効果は、n
の値が増加し続けるほど効果が低くなります。したがって、時間の複雑さはO(n^2)
と評価されます。