わかりました-ここにはいくつかの非常に良い答えがありますが、それらのほとんどすべてが同じ間違いを犯しているようで、一般的な使用法に浸透しています.
非公式に、f(n) = O( g(n) ) と書きます。スケーリング係数まで、n0 より大きいすべての n に対して、g(n) がf(n)より大きい場合です。つまり、f(n)は g(n)より速く成長しないか、g(n) によって上から制限されます。これは、f(n) が g(n) よりも悪くならないことが保証されているという事実を除けば、f(n) の成長速度については何も教えてくれません。
具体例: n = O( 2^n )。n の成長は 2^n よりもはるかに遅いことは誰もが知っているので、指数関数によって上で制限されていると言えます。n と 2^n の間には多くの余地があるため、厳密な境界ではありませんが、それでも正当な境界です。
なぜ私たち (コンピューター科学者) は正確ではなく境界を使用するのでしょうか? なぜなら、a) 境界はしばしば証明しやすく、b) アルゴリズムのプロパティを表現する簡単な方法を提供してくれるからです。私の新しいアルゴリズムが O(n.log n) であると言った場合、最悪の場合、実行時間は n 個の入力で n.log n によって上から制限されることを意味します (ただし、以下の私のコメントを参照してください)最悪の場合を意味しない場合があります)。
代わりに、関数が他の関数とまったく同じ速さで成長すると言いたい場合は、その点を示すためにthetaを使用します (マークダウンで f(n) の \Theta を意味するように T( f(n) ) と書きます) . T( g(n) ) は、g(n) によって上と下から制限されていることの省略形であり、ここでもスケーリング係数まで漸近的です。
つまり、f(n) = T( g(n) ) <=> f(n) = O(g(n)) および g(n) = O(f(n)) です。この例では、2^n != O(n) であるため、n != T( 2^n ) であることがわかります。
なぜこれについて心配するのですか?あなたの質問では、「誰かが O(x!) を書くためにクラックを吸わなければならないでしょうか?」と書いているからです。答えはノーです - 基本的にあなたが書くものはすべて階乗関数によって上から制限されるからです。クイックソートの実行時間は O(n!) です。厳密な制限ではありません。
ここにも別次元の繊細さがあります。通常、O( g(n) ) 表記を使用する場合、最悪の場合の入力について話しているため、複合ステートメントを作成しています。最悪の場合の実行時間では、g(n) を取るアルゴリズムよりも悪くはありません。 ) ステップ、再びモジュロ スケーリングと十分な大きさの n の場合。しかし、平均的な場合や最良の場合の実行時間について話したい場合もあります。
バニラのクイックソートは、いつものように、良い例です。最悪の場合は T( n^2 ) (実際には少なくとも n^2 ステップかかりますが、それ以上のステップは必要ありません) ですが、平均的なケースでは T(n.log n) です。ステップは n.log n に比例します。最良の場合は T(n.log n) でもありますが、たとえば、配列が既にソートされているかどうかを確認することで改善できます。この場合、最良の場合の実行時間は T( n ) になります。
これは、これらの境界の実際の実現に関するあなたの質問とどのように関連していますか? 残念ながら、O( ) 表記法は、実際の実装で処理しなければならない定数を隠してしまいます。したがって、たとえば T(n^2) 操作の場合、考えられるすべての要素のペアを訪問する必要があると言えますが、それらを何回訪問する必要があるかはわかりません (ただし、それは次の関数ではありません)。 n)。したがって、すべてのペアを 10 回または 10^10 回訪問する必要があり、T(n^2) ステートメントは区別しません。低次関数も隠されています.n^2 + 100n = T(n^2). O( ) 表記の背後にある考え方は、n が十分に大きい場合、n^2 が 100n よりもはるかに大きくなるため、これはまったく問題にならないということです。実行時間に対する 100n の影響に気付くことさえありません。しかし、私たちはしばしば「十分に小さい」 n を扱い、一定の要因などが実際の大きな違いを生むようにします。
たとえば、クイックソート (平均コスト T(n.log n)) とヒープソート (平均コスト T(n.log n)) はどちらも同じ平均コストのソート アルゴリズムですが、一般的にクイックソートはヒープソートよりもはるかに高速です。これは、ヒープソートがクイックソートよりも要素ごとにいくつかの比較を行うためです。
これは、O( ) 表記が役に立たないと言っているのではなく、単に不正確です。小さな n を扱うのは非常に鈍いツールです。
(この論文の最後の注意として、O( ) 表記は関数の成長を表すだけであることを思い出してください。時間である必要はありません。メモリ、分散システムで交換されるメッセージ、または関数に必要な CPU の数である可能性があります。並列アルゴリズム)