できるだけ形式的な定義はせず、単純な数学を好みます。
できるだけ形式的な定義はせず、単純な数学を好みます。
回答:
簡単に言うと、これはほぼ確実にBig O表記(上限)とTheta表記「which」(両側の上限)を混同しています。私の経験では、これは実際には非学術的な設定での議論の典型です。混乱を招きましたことをお詫び申し上げます。
Big Oの複雑さは、次のグラフで視覚化できます。
Big-O表記について私が言える最も簡単な定義は次のとおりです。
Big-O表記は、アルゴリズムの複雑さの相対的な表現です。
その文にはいくつかの重要で意図的に選ばれた単語があります。
- 相対:リンゴとリンゴのみを比較できます。算術乗算を行うアルゴリズムを、整数のリストをソートするアルゴリズムと比較することはできません。しかし、算術演算(1つの乗算、1つの加算)を実行する2つのアルゴリズムを比較すると、意味のあることがわかります。
- 表現: Big-O(最も単純な形式)は、アルゴリズム間の比較を1つの変数に減らします。その変数は、観察または仮定に基づいて選択されます。たとえば、並べ替えアルゴリズムは通常、比較演算(2つのノードを比較して相対的な順序を決定する)に基づいて比較されます。これは、比較にコストがかかることを前提としています。しかし、比較は安価ですがスワッピングが高価な場合はどうでしょうか?比較を変更します。そして
- 複雑さ: 10,000要素を並べ替えるのに1秒かかる場合、100万要素を並べ替えるのにどのくらい時間がかかりますか?この場合の複雑さは、他のものに対する相対的な尺度です。
残りを読んだら、戻って上記をもう一度読んでください。
Big-Oの最も良い例は、算術演算です。2つの数値(123456と789012)を取ります。学校で学んだ基本的な算術演算は次のとおりです。
- 添加;
- 減算;
- 乗算; そして
- 分割。
これらはそれぞれ操作または問題です。これらを解く方法をアルゴリズムと呼びます。
追加は最も簡単です。数値を(右側に)並べ、列に数字を追加して、その加算の最後の数値を結果に書き込みます。その数の「十」の部分は次の列に繰り越されます。
これらの数値の加算が、このアルゴリズムで最もコストのかかる操作であると仮定しましょう。これらの2つの数値を加算するには、6桁を加算する必要がある(そしておそらく7桁を運ぶ)ことは当然のことです。2つの100桁の数字を一緒に追加する場合、100を追加する必要があります。2つの 10,000桁の数字を追加する場合、10,000 を追加する必要があります。
パターンがわかりますか?複雑度(演算の数である)は、桁数に直接比例しているNより大きい数です。これをO(n)または線形複雑度と呼びます。
減算も同様です(ただし、キャリーではなく借りる必要がある場合があります)。
乗算は異なります。あなたは数字を並べて、一番下の数字の最初の数字を取り、それを順番に一番上の数字の各数字と掛け合わせます。したがって、2つの6桁の数値を乗算するには、36回の乗算を行う必要があります。最終結果を得るためにも、10列または11列の列の追加が必要になる場合があります。
2つの100桁の数値がある場合、10,000の乗算と200の加算を行う必要があります。200万桁の数字の場合、1兆(10 12)の乗算と200万の加算を行う必要があります。
アルゴリズムはn- 2乗でスケーリングするため、これはO(n 2)または2次の複雑度です。これは、別の重要な概念を紹介する良い機会です。
複雑さの最も重要な部分のみを気にします。
巧妙なことに、操作の数をn 2 + 2nと表現できることに気づいたかもしれません。しかし、100万桁の数値が2つある例からわかるように、2番目の項(2n)は重要ではなくなります(その段階での総演算の0.0002%を占めます)。
ここでは最悪のシナリオを想定していることがわかります。6桁の数値を乗算するとき、一方が4桁で、もう一方が6桁の場合、乗算は24回しかありません。それでも、「n」、つまり両方が6桁の数値の場合の最悪のシナリオを計算します。したがって、Big-O表記は、アルゴリズムの最悪のシナリオに関するものです。
次に考えられる最良の例は電話帳で、通常はホワイトページなどと呼ばれますが、国によって異なります。しかし、私は人を姓、次にイニシャルまたは名、おそらく住所、次に電話番号でリストしたものについて話しています。
1,000,000の名前が含まれている電話帳で "John Smith"の電話番号を検索するようにコンピュータに指示している場合、どうしますか?Sがどこから始まったのかを推測できるという事実(無視できないと仮定しましょう)を無視して、どうしますか?
典型的な実装は、真ん中まで開いて、50万分の1を取り、それを「スミス」と比較することです。たまたまそれが「スミス、ジョン」だったら、本当にラッキーだった。「ジョン・スミス」はその名前の前または後に来る可能性がはるかに高い。それが後である場合は、電話帳の後半を半分に分けて繰り返します。前の場合は、電話帳の前半を半分に分けて繰り返します。等々。
これはバイナリサーチと呼ばれ、気づいているかどうかにかかわらず、プログラミングで毎日使用されます。
したがって、100万人の名前の電話帳で名前を検索する場合、実際にはこれを最大20回実行することで任意の名前を検索できます。検索アルゴリズムを比較する際、この比較は「n」であると判断します。
- 3つの名前の電話帳の場合、2つの比較が必要です(多くても)。
- 7の場合、最大3つかかります。
- 15の場合は4です。
- …
- 1,000,000の場合、20かかります。
それは驚異的に良いですね。
Big-Oの用語では、これはO(log n)または対数の複雑さです。ここで、問題の対数は、ln(eを底とする)、log 10、log 2またはその他の底になります。O(2n 2)とO(100n 2)が両方ともO(n 2)であるのと同じように、それがまだO(log n)であるかどうかは関係ありません。
この時点で、Big Oを使用してアルゴリズムで3つのケースを判別できることを説明することは価値があります。
- 最良のケース:電話帳検索での最良のケースは、1つの比較で名前を見つけることです。これはO(1)または一定の複雑さです。
- 予想されるケース:上で説明したように、これはO(log n)です。そして
- 最悪の場合:これもO(log n)です。
通常、私たちは最良のケースを気にしません。予想される最悪のケースに関心があります。時々これらのどちらかがより重要になります。
電話帳に戻ります。
あなたが電話番号を持っていて、名前を見つけたい場合はどうなりますか?警察は逆電話帳を持っているが、そのような調査は一般大衆に否定されている。それとも彼らですか?技術的には、通常の電話帳の番号を逆に検索できます。どうやって?
名から始めて、番号を比較します。マッチした場合は、そうでない場合は、次に進みます。電話帳は(とにかく電話番号で)順序付けされていないため、この方法で行う必要があります。
したがって、電話番号を指定して名前を見つけるには(逆引き):
- 最良のケース: O(1);
- 予想されるケース: O(n)(500,000の場合); そして
- 最悪の場合: O(n)(1,000,000の場合)。
これはコンピュータサイエンスで非常に有名な問題であり、言及する価値があります。この問題では、N個の町があります。これらの各町は、一定の距離の道路によって1つ以上の他の町にリンクされています。巡回セールスマンの問題は、すべての町を訪れる最短のツアーを見つけることです。
簡単に聞こえますか?もう一度考えて。
3つの町A、B、Cがあり、すべてのペアの間に道路がある場合は、次のように移動できます。
- A→B→C
- A→C→B
- B→C→A
- B→A→C
- C→A→B
- C→B→A
まあ、実際にはそれより少ないです。これらのいくつかは同等です(たとえば、A→B→CとC→B→Aは同等です。たとえば、同じ道路を逆に使用しているためです)。
実際には、3つの可能性があります。
- これを4つの町に持っていけば、(iirc)12の可能性があります。
- 5では60です。
- 6は360になります。
これは、階乗と呼ばれる数学演算の関数です。基本的に:
- 5!= 5×4×3×2×1 = 120
- 6!= 6×5×4×3×2×1 = 720
- 7!= 7×6×5×4×3×2×1 = 5040
- …
- 25!= 25×24×…×2×1 = 15,511,210,043,330,985,984,000,000
- …
- 50!= 50×49×…×2×1 = 3.04140932×10 64
したがって、巡回セールスマン問題のBig-OはO(n!)または階乗または組み合わせの複雑さです。
200の町にたどり着くまでに、従来のコンピューターの問題を解決するのに十分な時間が宇宙に残っていません。
考えること。
私が簡単に触れておきたいもう1つの点は、O(n a)の複雑さをもつアルゴリズムは多項式複雑さを持っていると言われるか、多項式時間で解けるということです。
O(n)、O(n 2)などはすべて多項式時間です。一部の問題は多項式時間では解決できません。このため、世の中にはある物が使われています。公開鍵暗号化はその代表的な例です。非常に大きな数の2つの素因数を見つけることは計算上困難です。そうでなければ、私たちが使用している公開鍵システムは使用できません。
とにかく、ビッグO(改訂版)についての(できればプレーンな英語の)私の説明は以上です。
アルゴリズムが入力サイズに基づいてスケーリングする方法を示しています。
O(n 2):二次複雑度として知られています
アイテムの数は10倍に増加しますが、時間は10 2倍に増加します。基本的に、n = 10なので、O(n 2)は、スケーリング係数n 2である10 2を提供します。
O(n):線形複雑度として知られています
今回はアイテム数が10倍に増加し、時間も増加します。n = 10なので、O(n)の倍率は10です。
O(1):一定の複雑度として知られています
アイテム数は依然として10倍に増加していますが、O(1)のスケーリングファクターは常に1です。
O(log n):対数複雑度として知られています
計算回数は、入力値のログによってのみ増加します。したがって、この場合、各計算に1秒かかると仮定すると、入力のログはn
必要な時間ですlog n
。
それはそれの要点です。彼らは数学を削減するので、それは正確にn 2または彼らが言う何でもないかもしれませんが、それがスケーリングの支配的な要因になるでしょう。
Big-O表記法(「漸近的成長」表記法とも呼ばれます)は、原点付近の一定の要因やものを無視した場合の「外観」の関数です。どのようにスケールするかを説明するために使用します。
基本
「十分に」大きな入力の場合...
f(x) ∈ O(upperbound)
f
「より速く成長しない」ことを意味しますupperbound
f(x) ∈ Ɵ(justlikethis)
f
「まったく同じように成長する」という意味justlikethis
f(x) ∈ Ω(lowerbound)
f
「成長は遅くない」を意味しますlowerbound
big-O表記では、一定の要素は考慮されません。関数9x²
は「まったく同じように成長する」と言われてい10x²
ます。また、big-O 漸近記法は非漸近的なもの(「原点に近いもの」または「問題のサイズが小さい場合に何が起こるか」)を考慮しません。関数10x²
は「まったく同じように成長する」と言われ10x² - x + 2
ます。
なぜ方程式の小さな部分を無視したいのですか?より大きなスケールを考えると、それらは方程式の大部分によって完全に小さくなります。彼らの貢献は小さく、無関係です。(例のセクションを参照してください。)
別の言い方をすると、無限大に行くときの比率がすべてです。にかかる実際の時間をで除算O(...)
すると、大きな入力の制限において一定の係数が得られます。直感的にはこれは理にかなっています。一方を乗算してもう一方を取得できる場合、関数は互いに「スケーリング」します。それは私たちが言うとき...
actualAlgorithmTime(N) ∈ O(bound(N))
e.g. "time to mergesort N elements
is O(N log(N))"
...これは、「十分に大きい」問題サイズN(原点付近のものを無視する場合)には、次のような定数(たとえば、2.5、完全に構成されている)が存在することを意味します。
actualAlgorithmTime(N) e.g. "mergesort_duration(N) "
────────────────────── < constant ───────────────────── < 2.5
bound(N) N log(N)
定数には多くの選択肢があります。多くの場合、「最良の」選択はアルゴリズムの「定数係数」として知られていますが、最大でない項を無視するように、しばしば無視します(通常、重要ではない理由については、定数係数のセクションを参照してください)。また、上記の方程式を「最悪のシナリオではN*log(N)
、2.5倍以内(私たちがあまり気にしない一定の係数)以内で、おおよそよりも悪くなることはありません」と言って、限界と考えることもできます。 。
O(...)
ワーストケースの振る舞いを気にすることが多いため、一般的にはが最も便利です。f(x)
がプロセッサまたはメモリの使用量のような「悪い」ものを表す場合、「f(x) ∈ O(upperbound)
」はupperbound
、プロセッサ/メモリの使用量の最悪のシナリオであることを意味します。
用途
純粋に数学的構造として、big-O表記は処理時間とメモリの話に限定されません。これを使用して、以下のような、スケーリングが意味のあるあらゆるものの漸近性について議論できます。
N
パーティーの人々の間で可能なハンドシェイクの数(Ɵ(N²)
特にN(N-1)/2
、重要なのは、それが「のようにスケールする」ことですN²
)例
上記のハンドシェイクの例では、部屋にいる全員が他の全員の手を振っています。その例では、#handshakes ∈ Ɵ(N²)
。どうして?
少しバックアップしてください:ハンドシェイクの数はちょうどn-choose-2またはN*(N-1)/2
(N人はそれぞれN-1人の他の人と握手しますが、これはダブルカウントのハンドシェイクなので2で割ります):
ただし、非常に多くの人数の場合、線形項N
は小さめであり、比率に実質的に0を提供します(グラフでは、参加者の数が増えるにつれて、ボックス全体に対する対角線上の空のボックスの割合が小さくなります)。したがって、スケーリングの動作はorder N²
、またはハンドシェイクの数が「N²のように大きくなる」です。
#handshakes(N)
────────────── ≈ 1/2
N²
これは、グラフの対角線上の空のボックス(N *(N-1)/ 2チェックマーク)がそこにもなかったかのようです(N 2チェックマークは漸近的に)。
(「平易な英語」からの一時的な脱線:)これを自分で証明したい場合は、単純な代数を実行して比率を複数の用語に分割することができます(lim
つまり、「制限があると見なされている」ことを意味します。それを見たことがない、それは単に「そしてNは本当に本当に大きい」という表記です):
N²/2 - N/2 (N²)/2 N/2 1/2
lim ────────── = lim ( ────── - ─── ) = lim ─── = 1/2
N→∞ N² N→∞ N² N² N→∞ 1
┕━━━┙
this is 0 in the limit of N→∞:
graph it, or plug in a really large number for N
tl; dr:大きな値の場合、ハンドシェイクの数はx²に非常に似ているため、比率#handshakes /x²を書き留めると、正確に x²ハンドシェイクが必要ないという事実は表示されません。 10進数で任意の長い間。
たとえば、x = 1millionの場合、比率#handshakes /x²:0.499999 ...
直感の構築
これにより、次のようなステートメントを作成できます...
「十分な大きさのinputsize = Nの場合、定数係数が何であっても、入力サイズを2倍にすると ...
N →(2N)= 2(N)
N² →(2N)²= 4(N²)
cN³ →c(2N)³= 8(cN³)
c log(N) →c log(2N)=(c log(2))+(c log(N))=(固定量)+(c log(N))
c * 1 → c * 1
O(N 1.000001)未満であり、基本的に線形と呼んでもかまいません。
2 N →2 2N =(4 N)............別の言い方をすると...... 2 N →2 N + 1 = 2 N 2 1 = 2 2 N
[数学的に傾いている場合は、ネタバレにネタバレすることができます]
(https://stackoverflow.com/a/487292/711085へのクレジット付き)
(技術的には、一定の係数がより難解な例では問題になる可能性がありますが、私は上記のように(たとえば、log(N)で)そうしないように言いました)
これらは、プログラマーと応用コンピューター科学者が参照ポイントとして使用する、成長の大まかな順序です。彼らはいつもこれらを見ています。(つまり、「入力を2倍にすると、O(√N)アルゴリズムは1.414倍遅くなる」と技術的に考えることはできますが、「これは対数よりも悪いが、線形よりも優れている」と考えるのが良いでしょう。)
一定の要因
通常、特定の定数因子が何であるかは関係ありません。これらは関数の成長に影響を与えないためです。たとえば、2つのアルゴリズムはどちらもO(N)
完了するのに時間がかかる場合がありますが、一方が他方の2倍遅くなる場合があります。最適化はトリッキーなビジネスであるため、通常、要素が非常に大きくない限り、あまり気にしません(最適化は時期尚早ですか?)。また、より良いbig-Oを持つアルゴリズムを選択するという単なる行為は、しばしば桁違いにパフォーマンスを改善します。
いくつかの漸近的に優れたアルゴリズム(非比較O(N log(log(N)))
ソートなど)は、非常に大きな定数係数(例100000*N log(log(N))
:)、またはO(N log(log(N)))
非表示のように比較的大きなオーバーヘッドがあり+ 100*N
、「ビッグデータ」でも使用する価値はほとんどありません。
なぜO(N)があなたにできるベストなのか、つまりなぜデータ構造が必要なのか
O(N)
すべてのデータを読み取る必要がある場合、アルゴリズムはある意味で「最良の」アルゴリズムです。読書という行為データの束があるO(N)
操作。通常、メモリへのロードはO(N)
(ハードウェアサポートがある場合は高速で、すでにデータを読み取っている場合はまったく時間がかかりません)。ただし、すべてのデータ(または他のすべてのデータ)を触ったり、見たりすると、アルゴリズムはO(N)
この見回しに時間がかかります。実際のアルゴリズムにどれほど時間がかかるとしてもO(N)
、すべてのデータを調べるのにその時間を費やしたためです。
書くこと自体も同じことが言えます。Nのものを出力するすべてのアルゴリズムは、出力が少なくともその長さであるため、N時間かかります(たとえば、N個のトランプのセットを並べ替えて(再配置するために)出力することは階乗です:) O(N!)
。
これにより、データ構造の使用が促進されます。データ構造では、データを1回だけ(通常はO(N)
時間)読み取るだけでなく、任意の量の前処理(O(N)
or O(N log(N))
またはO(N²)
)を使用して、小さく保つ必要があります。その後、データ構造(挿入/削除など)の変更とデータに対するクエリの実行には、O(1)
またはのような非常に短い時間がかかりO(log(N))
ます。次に、多数のクエリを作成します。一般に、事前に実行する作業が多いほど、後で実行する必要のある作業は少なくなります。
たとえば、何百万もの道路セグメントの緯度と経度の座標があり、すべての交差点を検索したいとします。
O(N)
作業方法を1回だけ実行する必要はありませんが、何度も実行する場合(この場合は、N
各セグメントに対して1回)、O(N²)
仕事をする必要がある、または1000000²= 1000000000000操作。良くない(最近のコンピューターは1秒あたり約10億の操作を実行できる)。O(N)
時間内にすべてを前処理することで小さなコストを支払います。その後、そのキーで何かを検索するのに平均して一定の時間しかかかりません(この場合、キーは緯度と経度の座標であり、グリッドに丸められます。隣接するグリッドスペースは9つしかなく、絶え間ない)。O(N²)
から管理可能なものO(N)
になり、ハッシュテーブルを作成するためにわずかなコストを支払うだけで済みました。話の教訓:データ構造により、運用をスピードアップできます。さらに、高度なデータ構造により、驚くほど巧妙な方法で操作を結合、遅延、または無視することもできます。さまざまな問題にはさまざまな類推がありますが、それらはすべて、気になる、または簿記のために人為的に課した構造を利用する方法でデータを編成することを伴います。私たちは事前に作業を行い(基本的に計画と整理)、今や繰り返されるタスクははるかに簡単です!
実用例:コーディング中に成長の順序を視覚化する
漸近記法は、その中心において、プログラミングとはまったく別のものです。漸近表記法は、物事がどのようにスケーリングし、多くの異なる分野で使用できるかを考えるための数学的なフレームワークです。つまり、これは、コーディングに漸近表記を適用する方法です。
基本:サイズAのコレクション内のすべての要素(配列、セット、マップのすべてのキーなど)とやり取りするとき、またはループのA回の反復を実行するとき(サイズAの乗法因子) 。「乗算因子」と言うのはなぜですか?-ループと関数(ほぼ定義上)には乗算の実行時間があるため:反復回数、ループで実行された回数(または関数の場合:呼び出し回数)関数、時間は関数で行われます)。(これは、ループのスキップやループの早期終了など、特別なことをしない場合や、引数に基づいて関数の制御フローを変更する場合に当てはまります。これは、非常に一般的なものです)。
(ここで、x
sは一定時間の作業単位、プロセッサーの命令、インタープリターのオペコードなどを表します)
for(i=0; i<A; i++) // A * ...
some O(1) operation // 1
--> A*1 --> O(A) time
visualization:
|<------ A ------->|
1 2 3 4 5 x x ... x
other languages, multiplying orders of growth:
javascript, O(A) time and space
someListOfSizeA.map((x,i) => [x,i])
python, O(rows*cols) time and space
[[r*c for c in range(cols)] for r in range(rows)]
例2:
for every x in listOfSizeA: // A * (...
some O(1) operation // 1
some O(B) operation // B
for every y in listOfSizeC: // C * (...
some O(1) operation // 1))
--> O(A*(1 + B + C))
O(A*(B+C)) (1 is dwarfed)
visualization:
|<------ A ------->|
1 x x x x x x ... x
2 x x x x x x ... x ^
3 x x x x x x ... x |
4 x x x x x x ... x |
5 x x x x x x ... x B <-- A*B
x x x x x x x ... x |
................... |
x x x x x x x ... x v
x x x x x x x ... x ^
x x x x x x x ... x |
x x x x x x x ... x |
x x x x x x x ... x C <-- A*C
x x x x x x x ... x |
................... |
x x x x x x x ... x v
例3:
function nSquaredFunction(n) {
total = 0
for i in 1..n: // N *
for j in 1..n: // N *
total += i*k // 1
return total
}
// O(n^2)
function nCubedFunction(a) {
for i in 1..n: // A *
print(nSquaredFunction(a)) // A^2
}
// O(a^3)
少し複雑なことをしても、何が起こっているのかを視覚的に想像できるかもしれません。
for x in range(A):
for y in range(1..x):
simpleOperation(x*y)
x x x x x x x x x x |
x x x x x x x x x |
x x x x x x x x |
x x x x x x x |
x x x x x x |
x x x x x |
x x x x |
x x x |
x x |
x___________________|
ここで、描くことができる最小の認識可能なアウトラインが重要です。三角形は2次元形状(0.5 A ^ 2)であり、正方形が2次元形状(A ^ 2)であるのと同じです。ここで2の定数因数は2つの間の漸近比に残りますが、すべての因数と同様に無視します...(この手法には、ここでは触れませんが、いくつかの残念なニュアンスがあります。誤解を招く可能性があります。)
もちろん、これはループと関数が悪いことを意味するものではありません。それどころか、これらは現代のプログラミング言語の構成要素であり、私たちはそれらを愛しています。ただし、ループや関数、条件文をデータ(制御フローなど)と共に織り込む方法は、プログラムの時間とスペースの使用を模倣していることがわかります。時間とスペースの使用が問題になる場合は、賢明さに頼り、成長の順序を何らかの形で減らすために、私たちが考慮していなかった簡単なアルゴリズムまたはデータ構造を見つけます。それでも、これらの視覚化手法は(常に機能するとは限りませんが)、最悪の場合の実行時に単純な推測を与える可能性があります。
視覚的に認識できるもう1つの点を次に示します。
<----------------------------- N ----------------------------->
x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
x x x x x x x x x x x x x x x x
x x x x x x x x
x x x x
x x
x
これを並べ替えて、O(N)であることがわかります。
<----------------------------- N ----------------------------->
x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
x x x x x x x x x x x x x x x x|x x x x x x x x|x x x x|x x|x
または、O(N * log(N))の合計時間に対して、データのlog(N)パスを実行する場合があります。
<----------------------------- N ----------------------------->
^ x x x x x x x x x x x x x x x x|x x x x x x x x x x x x x x x x
| x x x x x x x x|x x x x x x x x|x x x x x x x x|x x x x x x x x
lgN x x x x|x x x x|x x x x|x x x x|x x x x|x x x x|x x x x|x x x x
| x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x
v x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x
無関係ですが、もう一度言及する価値があります。ハッシュ(たとえば、ディクショナリ/ハッシュテーブルルックアップ)を実行する場合、それはO(1)の要素です。それはかなり速いです。
[myDictionary.has(x) for x in listOfSizeA]
\----- O(1) ------/
--> A*1 --> O(A)
私たちは、このような再帰関数や分割統治アルゴリズムと同様に、非常に複雑な何かを、行う場合は、あなたが使用できるマスター定理を(通常は動作します)、またはばかばかしい例でAkra-Bazzi定理(ほとんど常に動作します)あなたは、ルックアップウィキペディアでのアルゴリズムの実行時間。
しかし、プログラマーはこのように考えません。結局のところ、アルゴリズムの直感はただの自然になってしまうからです。あなたは非効率的な何かをコーディングし始めて、すぐに「私は非常に非効率的な何かをしているのですか?」と考えます。答えが「はい」であり、それが実際に重要であると思われる場合は、一歩下がってさまざまなトリックを考え、処理を高速化できます(ほとんどの場合、答えは「ハッシュテーブルを使用する」、「ツリーを使用する」、非常にまれに、もう少し複雑です)。
償却および平均ケースの複雑さ
「償却」および/または「平均ケース」の概念もあります(これらは異なることに注意してください)。
平均的なケース:これは、関数自体ではなく、関数の期待値にbig-O表記を使用することにすぎません。すべての入力が等しく可能性があると考える通常のケースでは、平均ケースは実行時間の平均にすぎません。たとえば、クイックソートの場合、最悪のケースはO(N^2)
一部の本当に悪い入力に対するものですが、平均的なケースが通常ですO(N log(N))
(本当に悪い入力は数が非常に少ないため、平均的なケースでは気付かないほど少ないです)。
償却された最悪のケース:一部のデータ構造は、最悪の場合の複雑さが大きくなる可能性がありますが、これらの操作の多くを実行すると、実行する作業の平均量が最悪の場合よりも良くなることが保証されます。たとえば、通常一定のO(1)
時間がかかるデータ構造があるとします。ただし、場合O(N)
によっては、簿記やガベージコレクションなどを行う必要があるため、「しゃっくり」して1つのランダムな操作に時間がかかることがあります...しかし、しゃっくりが発生しても、Nで再度しゃっくりしないことを約束します。より多くの操作。最悪の場合のコストは依然としてO(N)
操作ごとですが、多くの実行での償却コストはO(N)/N
=O(1)
操作ごと。大きな操作は非常にまれであるため、大量の不定期の作業は、一定の要素として残りの作業と調和すると考えることができます。作品は、それが漸近的に消えるのに十分な数の呼び出しにわたって「償却」されると言います。
償却分析の類推:
あなたは車を運転します。たまに、ガソリンスタンドまで10分間費やしてから、1分間かけてタンクにガスを補充する必要があります。車でどこかに行くたびにこれを行った場合(ガソリンスタンドまで車で10分かかり、数秒でガロンの一部を埋める)、それは非常に非効率的です。しかし、数日おきにタンクを満タンにすると、ガソリンスタンドまでの運転に費やされた11分間は、十分な数のトリップで「償却」されるため、無視して、すべてのトリップがおそらく5%長くなるように見せかけることができます。
平均ケースと償却最悪ケースの比較:
ただし、攻撃者について合理的に心配している場合は、償却や平均ケースの他に、心配すべき他の多くのアルゴリズム攻撃ベクトルがあります。)
平均ケースと償却の両方は、スケーリングを考慮して設計するための非常に便利なツールです。
(このサブトピックに関心がある場合は、平均ケースと償却分析の違いを参照してください。)
多次元ビッグオー
ほとんどの場合、人々は複数の変数が機能していることを認識していません。たとえば、文字列検索アルゴリズムでは、アルゴリズムに時間がかかる場合がありますO([length of text] + [length of query])
。つまり、のような2つの変数では線形O(N+M)
です。他のもっと素朴なアルゴリズムは、O([length of text]*[length of query])
またはかもしれませんO(N*M)
。複数の変数を無視することは、アルゴリズム分析で見られる最も一般的な見落としの1つであり、アルゴリズムを設計するときに不利になることがあります。
一部始終
big-Oがすべてではないことに注意してください。キャッシュを使用して一部のアルゴリズムを大幅に高速化し、それらをキャッシュに気付かなくする、ディスクの代わりにRAMを使用する、並列化を使用する、または事前に作業を行うことでボトルネックを回避する-これらの手法は多くの場合独立している、成長順序とはです「big-O」表記。ただし、並列アルゴリズムのbig-O表記ではコアの数がよく表示されます。
また、プログラムの隠れた制約により、漸近的な振る舞いについてはあまり気にしないかもしれないことにも注意してください。制限された数の値を使用している可能性があります。次に例を示します。
O(N log(N))
クイックソート。挿入ソートを使用したい場合、これはたまたま小さな入力でうまく機能します。これらの状況は、多くの場合、分割統治アルゴリズムで発生します。このアルゴリズムでは、再帰的な並べ替え、高速フーリエ変換、行列の乗算など、問題をますます小さな副問題に分割します。実際には、同じまたは類似の漸近的パフォーマンスを持つアルゴリズムの間でも、それらの相対的なメリットは、実際には次のような他の要因によって駆動される可能性がありO(N log(N))
ます。実装の容易さなど、パフォーマンスに関する考慮事項。ライブラリが利用可能かどうか、およびライブラリの信頼性と維持管理の程度。
また、プログラムは、500 GHzコンピュータでは2 GHzコンピュータよりも遅く実行されます。実際の1秒あたりではなく、マシンリソース(たとえば、クロックサイクルごと)の観点からスケーリングを考えるため、これをリソース境界の一部とは見なしません。ただし、エミュレーションで実行しているかどうか、コンパイラがコードを最適化しているかどうかなど、パフォーマンスに「密かに」影響を与える可能性のある同様のものがあります。これらにより、一部の基本的な操作に時間がかかる場合があります(相互に関連する場合も)。また、一部の操作を漸近的に(相互に関連する場合も)スピードアップまたはスローダウンすることさえあります。影響は、実装や環境が異なれば、小さくても大きくてもかまいません。言語やマシンを切り替えて、余分な作業を省きますか?それは他の100の理由(必要性、スキル、同僚、プログラマーの生産性、
上記の問題は、使用するプログラミング言語の選択による影響と同様に、一定の要因の一部と見なされることはほとんどありません(そうである必要もありません)。しかし、時には(まれにですが)物事に影響を与える可能性があるため、それらに注意する必要があります。たとえばcpythonの場合、ネイティブの優先度キューの実装は漸近的に最適O(log(N))
ではありO(1)
ません(挿入またはfind-minの選択ではありません)。別の実装を使用していますか?おそらくそうではありません。Cの実装はおそらくより高速であり、おそらく他の場所にも同様の問題があるでしょう。トレードオフがあります。時には彼らは重要であり、時にはそうではありません。
(編集:「わかりやすい英語」の説明はここで終わります。)
数学の補遺
完全をf(x) ∈ O(g(x))
期すために、big-O表記の正確な定義は次のとおりです。つまり、「fはconst * gによって漸近的に上限がある」ことを意味します。xの有限値以下をすべて無視すると、のような定数が存在し|f(x)| ≤ const * |g(x)|
ます。(他の記号は次のとおりです:O
意味≤、Ω
意味≥ と同じです。小文字のバリエーションがあります:o
意味<、およびω
意味>。)f(x) ∈ Ɵ(g(x))
は、両方f(x) ∈ O(g(x))
を意味しますf(x) ∈ Ω(g(x))
(gの上限と下限):fのようないくつかの定数が存在しますとの間の「バンド」に常にconst1*g(x)
ありconst2*g(x)
ます。これは、あなたが作ることができる最も強い漸近的なステートメントであり、おおよそ==
。(申し訳ありませんが、明確にするために、絶対値記号の説明をこれまで延期することを選択しました。特に、コンピューターサイエンスのコンテキストで負の値が表示されるのを見たことがないためです。)
人々はしばしばを使用します= O(...)
。これはおそらくより正確な「comp-sci」表記であり、使用することは完全に正当です。"f = O(...)"は "f is order ... / f is xxx-bounded by ..."と読み、 "fは漸近性が...であるいくつかの式である"と考えられます。私はより厳密なものを使うように教えられました∈ O(...)
。∈
「の要素」であることを意味します(以前と同じように読みます)。この特定のケースでは、O(N²)
{ような要素を含む2 N²
、3 N²
、1/2 N²
、2 N² + log(N)
、- N² + N^1.9
、...}と無限大であるが、それはまだセットです。
OとΩは対称ではありません(n = O(n²)ですが、n²はO(n)ではありません)が、Ɵは対称であり、(これらの関係はすべて推移的かつ再帰的であるため)thereforeしたがって、対称的で推移的かつ再帰的です、したがって、すべての関数のセットを等価クラスに分割します。同等クラスとは、私たちが同じと考えるもののセットです。つまり、考えられる任意の関数を指定すると、クラスの正規/一意の「漸近的代表」を見つけることができます(一般に制限を設けることで...と思います)。すべての整数をオッズまたは偶数にグループ化できるのと同じように、Ɵを使用してすべての関数をx-ish、log(x)^ 2-ishなどにグループ化できます。基本的に、小さい項を無視します(ただし、それ自体が別のクラスである、より複雑な関数)。
=
表記は、より一般的なものであるかもしれないとさえ世界的に有名なコンピュータ科学者の論文で使用されています。さらに、カジュアルな環境では、人々はO(...)
彼らが何を意味するかを言うことがよくありƟ(...)
ます。一連の事柄Ɵ(exactlyThis)
はO(noGreaterThanThis)
...のサブセットなので、これは技術的に当てはまります。また、入力も簡単です。;-)
編集:クイックノート、これはほぼ確実にBig O表記(上限)とTheta表記(上限と下限の両方)を混同しています。私の経験では、これは実際には非学術的な設定での議論の典型です。混乱を招きましたことをお詫び申し上げます。
一文で:あなたの仕事のサイズが大きくなると、それを完了するのにどれくらい時間がかかりますか?
もちろん、これは入力として「サイズ」を使用し、出力として「所要時間」を使用しているだけです。メモリ使用量などについて話したい場合も同じ考え方が当てはまります。
これは、乾燥させたいN枚のTシャツの例です。それらを乾燥位置に配置するのは信じられないほど迅速であると想定します(つまり、人間の相互作用は無視できます)。もちろん、実際にはそうではありません...
外で洗濯ラインを使用する:無限に大きな裏庭があるとすると、洗濯はO(1)時間で乾燥します。どんなに手に入れても、同じ日差しと新鮮な空気が入るので、サイズは乾燥時間に影響しません。
タンブル乾燥機を使用する場合:各荷物に10枚のシャツを入れ、1時間後に完成させます。(ここの実際の数は無視してください—それらは無関係です。)したがって、50枚のシャツを乾燥させると、10枚のシャツを乾燥させる場合の約 5倍の時間がかかります。
すべてを通気性のある食器棚に入れる:すべてを1つの大きな山に入れて、一般的な暖かさだけをさせると、真ん中のシャツが乾くまでに長い時間がかかります。詳細は推測したくありませんが、これは少なくともO(N ^ 2)だと思います。洗浄負荷を増やすと、乾燥時間が速くなります。
「ビッグO」表記法の重要な側面の1つは、指定されたサイズに対してどのアルゴリズムがより高速になるかが示されていないことです。ハッシュテーブル(文字列キー、整数値)とペア(文字列、整数)の配列を取ります。文字列に基づいて、ハッシュテーブルのキーまたは配列の要素を見つける方が高速ですか?(つまり、配列の場合、「文字列部分が指定されたキーと一致する最初の要素を見つけます。」)ハッシュテーブルは通常、償却されます(〜=「平均して」)O(1)—一度設定すると、約かかります。 100エントリテーブルと1,000,000エントリテーブルのエントリを同時に検索します。(インデックスではなくコンテンツに基づいて)配列内の要素を見つけることは線形です。つまり、O(N)—平均して、エントリの半分を調べる必要があります。
これは、ルックアップ用の配列よりもハッシュテーブルを速くしますか?必ずしも。非常に小さなエントリのコレクションがある場合、配列の方がはるかに高速です。見ているもののハッシュコードを計算するのにかかる時間ですべての文字列をチェックできる場合があります。ただし、データセットが大きくなるにつれて、ハッシュテーブルは最終的に配列を上回ります。
Big Oは、入力が大きくなったときの、プログラムのランタイムなど、関数の拡張動作の上限を示します。
例:
O(n):入力サイズを2倍にすると、ランタイムは2倍になります
O(n 2):入力サイズがランタイムの4倍になる場合
O(log n):入力サイズが2倍になると、ランタイムは1増加します
O(2 n):入力サイズが1増えると、ランタイムは2倍になります
入力サイズは通常、入力を表すために必要なビット単位のスペースです。
Big O表記は、入力セットのサイズの関数として表される、計算(アルゴリズム)が完了するまでにかかるおおよその時間の目安として、プログラマーによって最も一般的に使用されます。
Big Oは、入力数が増加したときに2つのアルゴリズムがどれだけうまくスケールアップするかを比較するのに役立ちます。
より正確には、関数の漸近的な振る舞いを表すためにBig O表記が使用されます。これは、関数が無限大に近づいたときの動作を意味します。
多くの場合、アルゴリズムの「O」は次のいずれかの場合に該当します。
Big Oは、入力サイズが無限大に向かって増加するときに、関数の成長曲線に意味のある方法で寄与しない要素を無視します。つまり、関数に追加または乗算される定数は単に無視されます。
Big Oは、「自分のコードを実行するのにどのくらいの時間/スペースが必要か」という一般的な方法で自分を「表現」する方法にすぎません。
O(n)、O(n 2)、O(nlogn)などがよく表示されますが、これらはすべて表示する方法にすぎません。アルゴリズムはどのように変化しますか?
O(n)は、ビッグOがnであることを意味し、「nとは!」まあ「n」は要素の量です。アレイ内のアイテムを検索するイメージング。あなたは各要素を見て、「あなたは正しい要素/アイテムですか?」最悪の場合、項目は最後のインデックスにあります。つまり、リストに項目があるのと同じくらいの時間がかかったので、一般的に言えば、「ああ、nは与えられた値の量がかなり多いです!」 。
したがって、「n 2」が何を意味するかを理解しているかもしれませんが、さらに具体的に言うと、ソートアルゴリズムの最も単純な単純な考えを試してみてください。バブルソート。このアルゴリズムは、アイテムごとにリスト全体を調べる必要があります。
私のリスト
ここでの流れは次のようになります:
これはO n 2です。「n」個のアイテムがあるリストのすべてのアイテムを見る必要があるためです。各項目について、すべての項目をもう一度見て、比較のために、これも「n」なので、すべての項目について、「n」回見て、n * n = n 2を意味します。
これがあなたが望むように単純であることを願っています。
ただし、Big Oは、時間と空間の方法で自分を飛躍させる方法にすぎないことを覚えておいてください。
Big Oは、アルゴリズムの基本的なスケーリングの性質を説明します。
Big Oが特定のアルゴリズムについて教えてくれない情報はたくさんあります。それは骨を削ぎ、アルゴリズムのスケーリングの性質、特にアルゴリズムのリソース使用(時間またはメモリを考える)が「入力サイズ」に応じてどのようにスケーリングするかについての情報のみを提供します。
蒸気機関とロケットの違いを考えてみましょう。それらは同じものの異なる品種(たとえば、Priusエンジン対Lamborghiniエンジン)であるだけでなく、それらのコアでは劇的に異なる種類の推進システムです。蒸気エンジンはおもちゃのロケットよりも高速かもしれませんが、軌道ロケットの速度を達成できる蒸気ピストンエンジンはありません。これは、これらのシステムが、所定の速度(「入力サイズ」)に到達するために必要な燃料(「リソース使用量」)の関係に関して異なるスケーリング特性を持っているためです。
なぜこれがそれほど重要なのですか?ソフトウェアは、1兆までの要素によってサイズが異なる可能性がある問題を処理するためです。少し考えてみましょう。月への移動に必要な速度と人間の歩行速度の比率は10,000:1未満であり、ソフトウェアが直面する可能性のある入力サイズの範囲と比較すると、それは完全に小さなものです。また、ソフトウェアは入力サイズの天文学的な範囲に直面する可能性があるため、アルゴリズムのBig Oの複雑さの可能性があり、実装の詳細に勝つことは基本的なスケーリングの性質です。
正規のソートの例を考えてみましょう。バブルソートはO(n 2)で、マージソートはO(n log n)です。たとえば、バブルソートを使用するアプリケーションAとマージソートを使用するアプリケーションBの2つの並べ替えアプリケーションがあるとします。約30要素の入力サイズの場合、アプリケーションAはアプリケーションBよりも並べ替えで1,000倍高速です。30を超える要素をソートする必要がない場合は、アプリケーションAを選択する必要があることは明らかです。これは、これらの入力サイズではるかに高速であるためです。ただし、1千万個のアイテムを並べ替える必要がある場合は、各アルゴリズムのスケーリング方法が原因で、アプリケーションBが実際にはアプリケーションAよりも数千倍高速になることが予想されます。
Big-Oの一般的な品種を説明するときに私がよく使用するわかりやすい英語の獣医はここにあります
すべての場合において、リストの上位にあるアルゴリズムよりも上位にあるアルゴリズムを優先してください。ただし、より複雑なクラスに移動するコストは大きく異なります。
O(1):
成長無し。問題の大きさに関係なく、同じ時間で解決できます。これは、放送範囲内にいる人の数に関係なく、一定の距離で放送するのに同じ量のエネルギーを必要とする放送にいくらか類似しています。
O(log n):
この複雑さはO(1)と同じですが、少し悪いだけです。すべての実用的な目的で、これは非常に大きな一定のスケーリングと考えることができます。1000と10億のアイテムを処理する作業の違いは、ファクター6にすぎません。
O(n):
問題を解決するためのコストは、問題のサイズに比例します。問題のサイズが2倍になると、ソリューションのコストも2倍になります。ほとんどの問題は、データエントリ、ディスク読み取り、またはネットワークトラフィックとして何らかの方法でコンピュータにスキャンする必要があるため、これは一般的に手頃なスケーリング要素です。
O(n log n):
この複雑さはO(n)とよく似ています。実際には、この2つは同等です。このレベルの複雑さは、一般に依然としてスケーラブルと見なされます。仮定を微調整することにより、一部のO(n log n)アルゴリズムをO(n)アルゴリズムに変換できます。たとえば、キーのサイズを制限すると、ソートがO(n log n)からO(n)に減少します。
O(n 2):
正方形として成長します。nは正方形の1辺の長さです。これは「ネットワーク効果」と同じ成長率で、ネットワーク内の誰もがネットワーク内の他のすべての人を知っている可能性があります。成長は高価です。ほとんどのスケーラブルなソリューションでは、重要な体操を行わずにこのレベルの複雑さのアルゴリズムを使用することはできません。これは通常、他のすべての多項式の複雑さ-O(n k) -にも適用されます。
O(2 n):
スケーリングしません。重要なサイズの問題を解決することはできません。何を避けるべきかを知るのに、そして専門家がO(n k)にある近似アルゴリズムを見つけるのに役立ちます。
Big Oは、アルゴリズムがその入力のサイズと比較して使用する時間/空間の量の尺度です。
アルゴリズムがO(n)の場合、時間/空間はその入力と同じ割合で増加します。
アルゴリズムがO(n 2)の場合、時間/空間は入力の2乗の割合で増加します。
等々。
Big Oのわかりやすい英語の説明とは何ですか?可能な限り少ない正式な定義と単純な数学で。
Big-O表記法の必要性に関する簡単な英語の説明:
プログラムするとき、私たちは問題を解決しようとしています。私たちがコーディングしたものをアルゴリズムと呼びます。Big O表記を使用すると、アルゴリズムの悪いケースのパフォーマンスを標準化された方法で比較できます。ハードウェアの仕様は時間とともに変化し、ハードウェアの改善により、アルゴリズムの実行にかかる時間を短縮できます。ただし、ハードウェアを交換しても、アルゴリズムが変わらないため、アルゴリズムが時間の経過とともに改善または向上するわけではありません。したがって、さまざまなアルゴリズムを比較して、どちらが良いかを判断するために、Big O表記法を使用します。
Big O表記とは何かについてのわかりやすい英語の説明:
すべてのアルゴリズムが同じ時間で実行されるわけではなく、入力の項目数に基づいて変化する可能性があります。これをnと呼びます。これに基づいて、最悪のケース分析、またはnが次第に大きくなるときのランタイムの上限を検討します。Big O表記の多くはそれを参照しているため、nが何であるかを認識する必要があります。
ソフトウェアプログラムの速度を測定することは非常に困難であり、私たちが試した場合、答えは非常に複雑になり、例外や特別なケースでいっぱいになる可能性があります。これは大きな問題です。2つの異なるプログラムを比較して「最速」を見つけようとすると、これらすべての例外と特殊なケースが邪魔になり役に立たなくなるためです。
この役に立たない複雑さの結果として、人々は可能な限り最小かつ最も複雑でない(数学)式を使用してソフトウェアプログラムの速度を説明しようとします。これらの表現は非常に大雑把な概算です:少し運が良ければ、ソフトウェアの一部が高速か低速かの「本質」を捉えることができます。
これらは近似値であるため、表現に "O"(Big Oh)という文字を使用しています。これは、大幅に簡略化しすぎていることを読者に知らせるための規則です。(そして、表現が何らかの方法で正確であると誰もが誤って考えていないことを確認するため)。
「おお」を「ほぼ」または「おおよそ」という意味で読んだとしても、それほど間違ってはいません。(私はBig-Ohの選択がユーモアの試みであったかもしれないと思います)。
これらの "Big-Oh"式が実行しようとする唯一のことは、ソフトウェアが処理する必要のあるデータの量を増やすときにソフトウェアがどれだけ遅くなるかを説明することです。処理する必要のあるデータ量を2倍にした場合、ソフトウェアは作業を完了するのに2倍の時間を必要としますか?10倍ですか?実際には、遭遇して心配する必要があるBig-Oh式の数は非常に限られています。
いいもの:
O(1)
定数:入力の大きさに関係なく、プログラムの実行には同じ時間がかかります。O(log n)
対数:入力のサイズが大幅に増加しても、プログラムの実行時間はゆっくりと増加します。悪い人:
O(n)
線形:プログラムの実行時間は、入力のサイズに比例して増加します。O(n^k)
多項式:-入力のサイズが大きくなるほど、処理時間はますます速くなります-多項式関数として。...そして醜い:
O(k^n)
指数関数プログラムの実行時間は、問題のサイズが少しでも大きくなると、非常に急速に増加します。指数関数アルゴリズムを使用して小さなデータセットを処理することが現実的です。O(n!)
階乗プログラムの実行時間は、非常に小さく、最も簡単なように見えるデータセット以外の何かを待つ余裕があるよりも長くなります。O(n log n)
ます。
簡単な答えは次のとおりです。
ビッグOは、そのアルゴリズムで考えられる最悪の時間/空間を表します。アルゴリズムは、その制限を超えてより多くのスペース/時間を取ることはありません。Big Oは、極端な場合の時間/空間の複雑さを表します。
はい、2セントです。
Big-O、プログラムによって消費されるリソースの増加率、wrt problem-instance-size
リソース:合計CPU時間、最大RAM容量の可能性があります。デフォルトでは、CPU時間を指します。
問題は「合計を見つける」であるとしましょう。
int Sum(int*arr,int size){
int sum=0;
while(size-->0)
sum+=arr[size];
return sum;
}
problem-instance = {5,10,15} ==> problem-instance-size = 3、iterations-in-loop = 3
problem-instance = {5,10,15,20,25} ==> problem-instance-size = 5ループ内の反復= 5
サイズ「n」の入力の場合、プログラムは配列の「n」回の反復の速度で成長しています。したがって、Big-OはO(n)として表されるNです。
問題は「組み合わせを見つける」だとしましょう。
void Combination(int*arr,int size)
{ int outer=size,inner=size;
while(outer -->0) {
inner=size;
while(inner -->0)
cout<<arr[outer]<<"-"<<arr[inner]<<endl;
}
}
problem-instance = {5,10,15} ==> problem-instance-size = 3、total-iterations = 3 * 3 = 9
problem-instance = {5,10,15,20,25} ==> problem-instance-size = 5、total-iterations = 5 * 5 = 25
サイズ「n」の入力の場合、プログラムは配列の「n * n」回の反復の速度で成長しています。したがってBig-OはO(n 2)として表されるN 2です。
ビッグO表記は、空間または実行時間の観点からアルゴリズムの上限を記述する方法です。nは問題の要素の数(つまり、配列のサイズ、ツリー内のノードの数など)です。nが大きくなるときの実行時間について説明します。
あるアルゴリズムがO(f(n))であると言うとき、そのアルゴリズムによる実行時間(または必要なスペース)は常にいくつかの定数時間f(n)よりも低いことを意味します。
バイナリ検索の実行時間がO(logn)であると言うことは、log(n)を乗算できる定数cが存在するということです。これは常にバイナリ検索の実行時間よりも大きくなります。この場合、常にlog(n)比較の定数要素がいくつかあります。
つまり、g(n)がアルゴリズムの実行時間である場合、g(n)<= c * f(n)の場合、g(n)<= c * f(n)のとき、g(n)= O(f(n))となります。 cとkは定数です。
「Big Oのわかりやすい英語の説明とは何ですか?可能な限り形式的な定義を少なくし、単純な数学を使用してください。」
このように美しくシンプルで短い質問は、少なくとも生徒が個別指導の際に受け取るような、同じくらい短い回答に値するようです。
Big O表記は、入力データの量のみの観点から、アルゴリズムが実行できる時間*を単純に伝えます。
(*素晴らしい、単位のない時間感覚で!)
(**これは重要なことです。なぜなら、人々は今日または明日を生きるかどうかにかかわらず、常にもっと欲しくなるからです。)
さて、それがそうであるならば、Big O記法についてそれほど素晴らしいものは何ですか?
実用的に言えば、Big O はアルゴリズム自体の複雑さに真っ向から焦点を当て、JavaScriptエンジン、CPUの速度、インターネット接続など、単なる比例定数であるものを完全に無視するため、Big O分析は非常に有用かつ重要です。モデルTと同じように、すぐに笑えるほど古くなったものすべて。Big Oは、現在または未来に住んでいる人々と同じくらい重要な方法でのみパフォーマンスに焦点を当てています。
Big O表記はまた、コンピュータープログラミング/エンジニアリングの最も重要な原則に直接スポットライトを当てています。これは、優れたプログラマー全員が考え、夢を持ち続けるための刺激となる事実です。アルゴリズム。
アルゴリズムの例(Java):
// Given a list of integers L, and an integer K
public boolean simple_search(List<Integer> L, Integer K)
{
// for each integer i in list L
for (Integer i : L)
{
// if i is equal to K
if (i == K)
{
return true;
}
}
return false;
}
アルゴリズムの説明:
このアルゴリズムは、リストを項目ごとに検索し、キーを探します。
リスト内の各アイテムを反復処理し、それがキーである場合はTrueを返します。
キーが見つからずにループが終了した場合は、Falseを返します。
Big-O表記は、複雑さの上限(時間、空間など)を表します
The Big-O on Time Complexityを見つけるには:
最悪の場合にかかる時間(入力サイズに関して)を計算します。
最悪の場合:キーがリストに存在しません。
時間(最悪の場合)= 4n + 1
時間:O(4n + 1)= O(n)| Big-Oでは、定数は無視されます
O(n)〜線形
Best-Caseの複雑さを表すBig-Omegaもあります。
ベストケース:キーが最初の項目です。
時間(ベストケース)= 4
時間:Ω(4)= O(1)〜Instant \ Constant
C
良いだろう
ビッグオー
f(x)= O(g(x))は、xがa(たとえば、a = +∞)になるとき、次のような関数kがあることを意味します。
f(x)= k(x)g(x)
kはaの近傍に制限されます(a = +∞の場合、これは、すべてのx> Nに対して| k(x)| <MになるようなNおよびMの数があることを意味します)。
つまり、単純な英語では、f(x)= O(g(x))、x→aは、aの近傍で、fがgの積に分解されることを意味します。といくつかの有界関数のます。
小さいo
ちなみに、ここでは比較のための小oの定義を示します。
f(x)= o(g(x))は、xがaになるとき、次のような関数kがあることを意味します。
f(x)= k(x)g(x)
xがaになると、k(x)は0になります。
例
x→0の場合、sin x = O(x)。
sin x = O(1)(x→+∞の場合)
x 2 + x = O(x)、x→0の場合、
x 2 + x = O(x 2)(x→+∞の場合)
ln(x)= o(x)= O(x)(x→+∞の場合)。
注意!等号「=」の表記は「偽の等価性」を使用します。o(g(x))= O(g(x))はtrueですが、O(g(x))= o(gはfalseです(バツ))。同様に、 "ln(x)= o(x)when x→+∞"と記述しても問題ありませんが、式 "o(x)= ln(x)"は意味がありません。
その他の例
O(1)= O(n)= O(n 2)(n→+∞の場合(ただし逆ではない)、等式は「偽」)、
O(n)+ O(n 2)= O(n 2)(n→+∞の場合)
O(O(n 2))= O(n 2)(n→+∞の場合)
O(n 2)O(n 3)= O(n 5)(n→+∞の場合)
ウィキペディアの記事は次のとおりです。https://en.wikipedia.org/wiki/Big_O_notation
Big O表記は、任意の数の入力パラメーター(「n」と呼ぶ)が与えられた場合に、アルゴリズムがどれだけ速く実行されるかを説明する方法です。さまざまなマシンがさまざまな速度で動作するため、コンピューターサイエンスで役立ちます。4.5ギガヘルツのオクトコアプロセッサーを搭載したシステムを実行している場合でも、私は実行している可能性があるため、アルゴリズムに5秒かかると言ってもあまり意味がありません。 15年前の800 Mhzシステム。アルゴリズムに関係なく、さらに時間がかかる可能性があります。したがって、時間の観点からアルゴリズムの実行速度を指定する代わりに、入力パラメータの数、つまり "n"の観点からアルゴリズムの実行速度を指定します。このようにアルゴリズムを記述することにより、コンピュータ自体の速度を考慮する必要なく、アルゴリズムの速度を比較できます。
私はこの主題にさらに貢献しているのかわかりませんが、共有したいと思っていました。このブログの投稿には、Big Oに関する非常に役立つ(非常に基本的ではありますが)説明と例があります。
例を通して、これはべっ甲のようなべっ甲のような頭蓋骨を理解するのに役立ちました。ですから、正しい方向に向かわせるには、10分のかなりの読み物だと思います。
あなたはビッグオーを知るためにあるすべてを知りたいですか?私もそうです。
したがって、ビッグOについては、1ビートだけの単語を使用します。単語ごとに1つの音。小さな言葉は速いです。あなたはこれらの言葉を知っていますし、私もそうです。1つの音の言葉を使用します。彼らは小さい。私たちが使用するすべての単語を知っていると思います!
さて、あなたと私が仕事について話しましょう。ほとんどの場合、私は仕事が好きではありません。あなたは仕事が好きですか?あなたがそうしているのかもしれませんが、私はそうしていないと確信しています。
私は仕事に行きたくない。私は仕事で時間を過ごすのが好きではありません。自分のやり方ができたら、ただ遊んで、楽しいことをしたいです。私と同じように感じますか?
時々、私は仕事に行かなければなりません。悲しいですが、本当です。だから、私が仕事をしているとき、私にはルールがあります:私はより少ない仕事をしようとします。できる限り仕事に近づかない。その後、遊びに行きます!
だからここに大きなニュースがあります:大きなOは私が仕事をしないように助けることができます!大きなOを知っていれば、もっと多くの時間をプレイできます。それが大きなOのおかげです。
今、仕事があります。私はこのリストを持っています:1、2、3、4、5、6。このリストにすべてのものを追加する必要があります。
うわー、仕事嫌いです。しかし、まあ、私はこれをしなければなりません。だからここに行きます。
1プラス2は3です。プラス3は6です...そして4は...わかりません。道に迷った。頭の中で行うのは難しい。私はこの種の仕事をあまり気にしません。
だから、仕事をしないようにしましょう。あなたと私はそれがどれほど難しいか考えてみましょう。6つの数値を追加するには、どれくらいの作業が必要ですか?
さて、見てみましょう。1と2を追加し、それを3に追加してから、4に追加する必要があります。全体で、6つの追加を数えます。これを解決するには、6つの追加を行う必要があります。
ここに大きなOがあり、この数学がどれほど難しいかを教えてくれます。
Big Oは言う:これを解決するには6つの追加を行う必要があります。1から6まで、それぞれ1つずつ追加します。6つの小さな作業...作業の各ビットは1つの追加です。
さて、今はそれらを追加する作業は行いません。しかし、私はそれがどれほど難しいかを知っています。6回の追加になります。
ああ、いや、今はもっと仕事がある。シーッシュ。誰がこの種のものを作るのですか?
今、彼らは私に1から10まで追加するように頼みます!なぜそれをするのですか?1から6を追加したくありませんでした。1から10まで追加する…ええと…それはさらに難しいでしょう!
それはどれほど難しいでしょうか?どれだけ多くの作業を行う必要がありますか?手順の増減は必要ですか?
ええと、私は10の加算を行う必要があると思います... 1から10まで、それぞれに1つずつ。十は六以上です。1から6ではなく、1から10を追加するには、もっと多くの作業が必要になります。
今は追加したくありません。それだけ追加するのがどれほど難しいか考えたいだけです。そして、できるだけ早くプレイしたいと思っています。
1から6まで追加するのは、それがいくらかの作業です。しかし、1から10を追加すると、それはより多くの作業になると思いますか?
Big Oはあなたの友人であり、私のものです。Big Oは、計画を立てるために、私たちがやらなければならない仕事の量を考えるのに役立ちます。そして、もし私たちがビッグオーの友達なら、彼は私たちがそれほど難しくない仕事を選ぶのを手伝ってくれるでしょう!
今、私たちは新しい仕事をしなければなりません。大野。私はこの仕事が全然好きではありません。
新しい作業は、1からnまでのすべてのものを追加します。
待つ!nとは何ですか?私はそれを見逃しましたか?nが何であるかを教えてくれない場合、どうすれば1からnに追加できますか?
えーと、nが何なのかわかりません。言われなかった。あなたでしたか?番号?しかたがない。だから私たちは仕事をすることができません。ふew。
しかし、今はその仕事をしませんが、nを知っていれば、それがどれほど難しいかを推測できます。n個を合計する必要がありますよね?もちろん!
ここで大きなOが登場し、彼はこの作業がどれほど難しいかを教えてくれます。彼は言う:1からNにすべてのものを1つずつ追加することはO(n)です。これらすべてを追加するには、[n回追加する必要があることはわかっています。] [1]これは大きなOです。彼はある種の仕事をするのがどれほど難しいかを教えてくれます。
私にとって、私はビッグOを大きくてゆっくりとした上司の男のように思います。彼は仕事について考えていますが、しません。彼は「その仕事は速い」と言うかもしれません。または、彼は「その仕事はとても遅くて難しいです!」と言うかもしれません。しかし、彼は仕事をしません。彼はただその仕事を見て、それからどれだけの時間がかかるかを教えてくれます。
私は大きなOをたくさん気にします。なぜですか?働きたくない!誰も仕事が好きではありません。それが、私たち全員が大きなOを愛する理由です。彼は私たちがどれだけ速く働くことができるかを教えてくれます。彼は私たちがどれほど大変な仕事をしているのかを考えるのを助けてくれます。
ああ、もっと仕事。今、仕事をしましょう。しかし、それを段階的に行う計画を立てましょう。
彼らは私たちに10枚のカードのデッキを与えた。それらはすべて混同されています:7、4、2、6 ...まったくまっすぐではありません。そして今...私たちの仕事はそれらを分類することです。
ああ。それは大変な作業のように思えます!
このデッキをどのように分類できますか?私は計画があります。
最初から最後まで、カードの各ペアを、ペアごとに、デッキを通して見ていきます。あるペアの最初のカードが大きく、そのペアの次のカードが小さい場合は、それらを交換します。そうでなければ、私は次のペアに行き、以下同様に続きます...そしてすぐに、デッキは完成しました。
デッキが完了したら、私は尋ねます:そのパスでカードを交換しましたか?もしそうなら、上からもう一度やり直さなければなりません。
いつか、いつかスワップがなくなり、私たちのデッキが完成するでしょう。大変な仕事です!
さて、それらのルールでカードを並べ替えるのにどれだけの作業が必要でしょうか?
私はカードを10枚持っています。そして、ほとんどの場合、つまり、運が悪い場合は、デッキ全体を最大10回通過しなければならず、毎回最大10枚のカードを交換する必要があります。
ビッグオー、助けて!
ビッグOが入って言った:n枚のカードのデッキの場合、この方法でソートするにはO(N二乗)時間で行われます。
なぜ彼はn二乗と言うのですか?
まあ、あなたはnの2乗がnのn倍であることを知っています。さて、私はそれを手に入れました:n枚のカードがチェックされました。これは、それぞれnステップの2つのループです。これは、実行する必要がある多くの作業の2乗です。確かにたくさんの仕事です!
大きなOがO(n二乗)の作業が必要だと言ったとき、彼は鼻の上でn二乗加算を意味するのではありません。場合によっては、少し小さいかもしれません。しかし、最悪の場合、デッキを並べ替えるのにn乗の作業が必要になります。
ここで、ビッグOが私たちの友達です。
Big Oはこれを指摘します。nが大きくなると、カードを並べ替えると、ジョブは古いものを追加するだけのジョブよりもはるかに難しくなります。どうやってこれを知るのですか?
まあ、nが実際に大きくなったとしても、nやnの2乗に何を追加してもかまいません。
大きなnの場合、nの2乗はnよりも大きくなります。
Big Oは、物事を分類することは物事を追加することよりも難しいことを教えてくれます。O(nの2乗)は、大きなnのO(n)よりも大きいです。つまり、nが実際に大きくなった場合、n個の混合されたデッキを並べ替えるには、単にn個の混合されたものを追加するよりも時間がかかる必要があります。
Big Oは私たちの仕事を解決しません。Big Oは、作業がいかに難しいかを教えてくれます。
カードの山があります。私はそれらを分類しました。あなたは助けた。ありがとう。
カードをソートするより速い方法はありますか?ビッグオーは私たちを助けることができますか?
はい、もっと速い方法があります!学ぶには少し時間がかかりますが、うまくいきます...そしてそれはかなり速く働きます。あなたもそれを試すことができますが、各ステップで時間をかけ、あなたの場所を失うことはありません。
デッキを並べ替えるこの新しい方法では、以前のようにカードのペアをチェックしません。このデッキを並べ替えるための新しいルールは次のとおりです。
1:今取り組んでいるデッキの部分で1枚のカードを選びます。必要に応じて、1つを選択できます。(これを初めて行うときは、「現在取り組んでいるデッキの一部」はもちろん、デッキ全体です。)
二:私はあなたが選んだそのカードのデッキをプレイする。このスプレイは何ですか。どうすればスプレーできますか?さて、スタートカードから順に1枚ずつ行き、スプレイカードよりも高いカードを探します。
3つ目:最後のカードから上に行き、スプレイカードよりも低いカードを探します。
これらの2枚のカードを見つけたら、それらを交換し、さらに交換するカードを探します。つまり、ステップ2に戻り、選択したカードをさらに広げます。
ある時点で、このループ(2から3へ)は終了します。この探索の両半分がスプレイカードで出会うと終了します。次に、ステップ1で選択したカードでデッキを展開しました。さて、スタート近くのすべてのカードは、スプレイカードよりも低くなっています。終わり近くのカードは、スプレイカードよりも高いです。クールなトリック!
4つ(そしてこれは面白い部分です):私は2つの小さなデッキを持っています。次に、各小さなデッキでステップ1に進みます。つまり、最初の小さなデッキのステップ1から開始し、その作業が完了すると、次の小さなデッキのステップ1から開始します。
デッキをいくつかの部分に分けて、各部分をより小さく、より小さくソートし、時にはやらなければならない仕事はもうありません。今、これはすべてのルールで遅く見えるかもしれません。しかし、私を信じてください、それはまったく遅くありません。それは物事を分類する最初の方法よりもはるかに少ない作業です!
この種の名前は何ですか?クイックソートと呼ばれています!その並べ替えはCAR Hoareと呼ばれる男によって行われ、彼はそれをQuick Sortと呼びました。現在、クイックソートは常に使用されています。
クイックソートは大きなデッキを小さなものに分割します。つまり、大きなタスクを小さなタスクに分割します。
うーん。そこにはルールがあるのではないでしょうか。大きなタスクを小さくするには、それらを分割します。
この種は非常に迅速です。どれくらい速い?Big Oは私たちに言っています:この場合、この場合、O(n log n)の作業が必要です。
それは最初の種類よりも多少速いですか?ビッグオー、助けてください!
最初のソートはO(n二乗)でした。ただし、クイックソートはO(n log n)です。n log nがnの2乗よりも小さいことを知っていますか?さて、クイックソートが高速であることは、そのためです。
デッキを並べ替える必要がある場合、最良の方法は何ですか?まあ、あなたはあなたがやりたいことをすることができますが、私はクイックソートを選びます。
なぜクイックソートを選択するのですか?もちろん仕事は嫌いです!できる限り早く仕事を終わらせたい。
クイックソートの作業が少ないことをどのように確認できますか?O(n log n)がO(nの2乗)よりも小さいことを知っています。Oの方が小さいので、クイックソートの作業は少なくなります。
さて、あなたは私の友人、ビッグオーを知っています。彼は私たちがより少ない仕事をするのを助けます。ビッグOを知っていれば、仕事も減ります!
あなたは私と一緒にすべてを学びました!あなたはとても賢いです!どうもありがとうございます!
これで作業は完了です。遊びに行きましょう!
[1]:1からnまでのすべてのものを一度にすべてカンニングして追加する方法があります。ガウスという名前の子供は、8歳のときにこれを見つけました。私はそれほど賢くないので、彼がどうやってそれをしたのか私に尋ねないでください。
時間の複雑さを理解するためのより簡単な方法があります。時間の複雑さを計算するための最も一般的なメトリックはBig O表記です。これにより、すべての定数係数が削除されるため、Nが無限に近づくときに、Nに関連して実行時間を推定できます。一般的には、次のように考えることができます。
statement;
一定です。ステートメントの実行時間はNに関連して変化しません
for ( i = 0; i < N; i++ )
statement;
線形です。ループの実行時間はNに正比例します。Nが2倍になると、実行時間も2倍になります。
for ( i = 0; i < N; i++ )
{
for ( j = 0; j < N; j++ )
statement;
}
二次式です。2つのループの実行時間はNの2乗に比例します。Nが2倍になると、実行時間はN * N増加します。
while ( low <= high )
{
mid = ( low + high ) / 2;
if ( target < list[mid] )
high = mid - 1;
else if ( target > list[mid] )
low = mid + 1;
else break;
}
対数です。アルゴリズムの実行時間は、Nを2で除算できる回数に比例します。これは、アルゴリズムが各反復で作業領域を半分に分割するためです。
void quicksort ( int list[], int left, int right )
{
int pivot = partition ( list, left, right );
quicksort ( list, left, pivot - 1 );
quicksort ( list, pivot + 1, right );
}
N *ログ(N)です。実行時間は対数であるNループ(反復または再帰)で構成されているため、アルゴリズムは線形と対数の組み合わせです。
一般に、1次元のすべてのアイテムで何かを行うことは線形であり、2次元のすべてのアイテムで何かを行うことは2次であり、作業領域を半分に分割することは対数です。立方、指数、平方根などの他のBig O測度もありますが、それほど一般的ではありません。ビッグO表記はO()として記述されます。ここで、はメジャーです。クイックソートアルゴリズムはO(N * log(N))として記述されます。
注:これは、最良、平均、および最悪のケースの測定値を考慮していません。それぞれに独自のBig O表記があります。また、これは非常に単純化した説明です。Big Oが最も一般的ですが、これまでに示したよりも複雑です。ビッグオメガ、リトルオー、ビッグシータなどの他の表記法もあります。おそらく、アルゴリズム分析コース以外ではこれらに遭遇しないでしょう。
あなたがハリー・ポッター:アマゾンから完全な8フィルムコレクション[ブルーレイ]を注文し、同時に同じフィルムコレクションをオンラインでダウンロードするとします。あなたはどちらの方法がより速いかをテストしたいです。配信には約1日かかり、ダウンロードは約30分早く完了しました。すごい!ですからタイトなレースです。
ロードオブザリング、トワイライト、ダークナイトトリロジーなどのブルーレイ映画を注文して、すべての映画を同時にオンラインでダウンロードするとどうなりますか?今回は、配信が完了するまでに1日かかりますが、オンラインダウンロードが完了するまでに3日かかります。オンラインショッピングの場合、購入数(投入数)は配達時間に影響しません。出力は一定です。これをO(1)と呼びます。
オンラインダウンロードの場合、ダウンロード時間は映画のファイルサイズ(入力)に正比例します。これをO(n)と呼びます。
実験から、オンラインショッピングはオンラインダウンロードよりも優れていることがわかります。ビッグO表記は、アルゴリズムのスケーラビリティと効率を分析するのに役立つため、理解することは非常に重要です。
注: Big O表記は、アルゴリズムの最悪のシナリオを表しています。O(1)とO(n)が上記の例の最悪のシナリオであると仮定しましょう。
リファレンス:http : //carlcheo.com/compsci
サイズnのデータセットで何かを行うアルゴリズムAについて話していると仮定します。
次にO( <some expression X involving n> )
、簡単な英語で:
Aの実行時に運が悪ければ、完了するまでにX(n)回の演算が必要になる可能性があります。
偶然にも、特定の機能(と考えるがあるの実装のX(n)はかなり頻繁に発生する傾向があります)。これらは周知であり、容易に比較される(例:1
、Log N
、N
、N^2
、N!
、など。)
話をするとき、これらを比較することによってAと他のアルゴリズム、それは操作の回数に応じてアルゴリズムをランク付けすることが容易であることができる(最悪の場合)を完了する必要があります。
一般的に、私たちの目標は、アルゴリズムAX(n)
をできるだけ少ない数で返す関数を持つような方法で、アルゴリズムAを見つけるか、構造化することです。
あなたの頭に無限の適切な概念がある場合、非常に簡単な説明があります:
Big O表記は、無限に大きな問題を解決するためのコストを示します。
そしてさらに
一定の要因は無視できる
アルゴリズムを2倍の速さで実行できるコンピューターにアップグレードした場合、大きなO表記はそれに気付きません。定数係数の改善は小さすぎて、ビッグO表記が機能するスケールでは気付かれないほどです。これは、ビッグO表記の設計の意図的な部分であることに注意してください。
ただし、一定の要因よりも「大きい」ものはすべて検出できます。
サイズが「無限大」であり、ほぼ無限大と見なされる計算を行うことに関心がある場合、大きなO表記法はおおよそ問題を解決するためのコストです。
上記が意味をなさない場合、頭に無限の互換性のある直感的な概念がないため、おそらく上記のすべてを無視する必要があります。これらのアイデアを厳密にするため、またはまだ直感的に役に立たない場合に説明するために私が知っている唯一の方法は、最初に大きなO表記または類似のものを教えることです。(ただし、将来的に大きなO表記法をよく理解したら、これらのアイデアを再検討する価値があるかもしれません)
「Big O」表記のわかりやすい英語の説明とは何ですか?
非常に簡単なメモ:
「ビッグO」のOは「順序」(または正確には「順序」)と
呼ばれるため、比較するために何かを順序付けるために使用されるという文字通りの考えを得ることができます。
「Big O」は2つのことを行います。
Notations
。最もよく使用される表記は7つあります
1
ステップでタスクを実行することを意味します。優れています。注文番号1です。logN
ステップでタスクを完了することを意味します。N
ステップ、そのフェア、注文番号3でタスクを完了O(NlogN)
ステップでタスクを終了します、それは良くありません、注文番号4N^2
ステップでタスクを実行する、それは悪い、注文番号52^N
ステップでタスクを実行する、それは恐ろしい、注文番号6N!
ステップでタスクを完了する、ひどい、注文番号7表記法を使用するとします。O(N^2)
メソッドがタスクを実行するためにN * Nステップを実行することが明確であるだけでなくO(NlogN)
、ランク付けからも良くないことがわかります。
理解を深めるために、行末の順序に注意してください。すべての可能性を考慮すると、7つを超える表記があります。
CSでは、タスクを実行する一連のステップはアルゴリズムと呼ばれます。
用語では、Big O表記は、アルゴリズムのパフォーマンスまたは複雑さを表すために使用されます。
さらに、Big Oは最悪のケースを確立するか、上限ステップを測定します。
最良の場合については、Big-Ω(Big-Omega)を参照してください。
Big-Ω(Big-Omega)表記(記事)| カーンアカデミー
まとめ
「Big O」は、アルゴリズムのパフォーマンスを説明し、評価します。
またはそれを正式に扱う場合、「Big O」はアルゴリズムを分類し、比較プロセスを標準化します。
それを見る最も簡単な方法(平易な英語)
入力パラメーターの数がアルゴリズムの実行時間にどのように影響するかを確認しようとしています。アプリケーションの実行時間が入力パラメーターの数に比例する場合、それはBig O of nであると言われます。
上記のステートメントは良いスタートですが、完全に真実ではありません。
より正確な説明(数学)
と思います
n =入力パラメーターの数
T(n)=アルゴリズムの実行時間をnの関数として表す実際の関数
c =定数
f(n)=アルゴリズムの実行時間をnの関数として表す近似関数
次に、Big Oに関する限り、次の条件が満たされる限り、近似f(n)は十分に良いと見なされます。
lim T(n) ≤ c×f(n)
n→∞
方程式は、nが無限大に近づくときに読み取られます。nのTは、nのfのc倍以下です。
大きなO表記では、これは
T(n)∈O(n)
これは、n of Tがn of big Oにあるため読み取られます。
英語に戻る
上記の数学的な定義に基づいて、アルゴリズムがnのBig Oであると言う場合、それはn(入力パラメーターの数)以上の関数であることを意味します。アルゴリズムがnのビッグOの場合、それは自動的にnのビッグOの正方形になります。
nの大きなOは、私のアルゴリズムが少なくともこれと同じ速度で実行されることを意味します。アルゴリズムのBig O表記を見て、遅いとは言えません。あなたはその速く言うことができます。
チェックこれを UCバークレーからビッグOのビデオチュートリアルのために。それは実際には単純な概念です。Shewchuck教授(別名神レベルの教師)が説明しているのを聞くと、「ああ、それだけです!」と言うでしょう。
特に数学にあまり詳しくない人のために、大きなO表記について本当に素晴らしい説明を見つけました。
https://rob-bell.net/2009/06/a-beginners-guide-to-big-o-notation/
ビッグO表記は、コンピューターサイエンスでアルゴリズムのパフォーマンスや複雑さを表すために使用されます。Big Oは最悪のシナリオを具体的に説明し、アルゴリズムによって必要な実行時間または使用されるスペース(メモリやディスクなど)を説明するために使用できます。
プログラミングパールやその他のコンピュータサイエンスの本を読んでいて、数学の基礎がない人は、O(N log N)やその他の奇妙な構文に言及している章に達したときに壁にぶつかるでしょう。うまくいけば、この記事がBig Oと対数の基本を理解するのに役立つことを願っています。
プログラマーとして最初に、そして数学者として2番目(多分3番目または4番目)に、私はBig Oを完全に理解する最良の方法は、コードでいくつかの例を作成することであると考えました。したがって、以下は、可能な場合の説明と例とともに、いくつかの一般的な成長の順序です。
O(1)
O(1)は、入力データセットのサイズに関係なく、常に同じ時間(またはスペース)で実行されるアルゴリズムを記述します。
bool IsFirstElementNull(IList<string> elements) { return elements[0] == null; }
オン)
O(N)は、パフォーマンスが線形に、入力データセットのサイズに正比例して成長するアルゴリズムを表します。以下の例は、Big Oが最悪の場合のパフォーマンスシナリオをどのように優先するかも示しています。forループの反復中に一致する文字列が見つかると、関数は早く戻りますが、Big O表記では、アルゴリズムが最大反復回数を実行する上限が常に想定されます。
bool ContainsValue(IList<string> elements, string value) { foreach (var element in elements) { if (element == value) return true; } return false; }
O(N 2)
O(N 2)は、パフォーマンスが入力データセットのサイズの2乗に正比例するアルゴリズムを表します。これは、データセットのネストされた反復を含むアルゴリズムで一般的です。より深くネストされた反復はO(N 3)、O(N 4)などになります。
bool ContainsDuplicates(IList<string> elements) { for (var outer = 0; outer < elements.Count; outer++) { for (var inner = 0; inner < elements.Count; inner++) { // Don't compare with self if (outer == inner) continue; if (elements[outer] == elements[inner]) return true; } } return false; }
O(2 N)
O(2 N)は、入力データセットへの各追加で成長が倍になるアルゴリズムを示します。O(2 N)関数の成長曲線は指数関数的です-非常に浅いところから始まり、その後気象的に上昇します。O(2 N)関数の例は、フィボナッチ数列の再帰的な計算です。
int Fibonacci(int number) { if (number <= 1) return number; return Fibonacci(number - 2) + Fibonacci(number - 1); }
対数
対数の説明は少し難しいので、一般的な例を使用します。
バイナリ検索は、ソートされたデータセットを検索するために使用される手法です。これは、データセットの中央の要素、基本的には中央値を選択することで機能し、それをターゲット値と比較します。値が一致する場合、成功を返します。ターゲット値がプローブ要素の値よりも高い場合は、データセットの上半分を取得し、それに対して同じ操作を実行します。同様に、ターゲット値がプローブ要素の値よりも低い場合は、下半分に対して操作を実行します。値が見つかるまで、またはデータセットを分割できなくなるまで、反復ごとにデータセットを半分にします。
このタイプのアルゴリズムは、O(log N)として記述されます。バイナリサーチの例で説明されているデータセットの反復的な半減は、最初にピークに達し、データセットのサイズが増加するにつれてゆっくりと平らになる成長曲線を生成します。 100アイテムを含む場合は2秒かかり、1000アイテムを含むデータセットは3秒かかります。入力データセットのサイズを2倍にしても、アルゴリズムの1回の反復後にデータセットが半分になり、したがって、入力データセットのサイズの半分と同等になるため、その成長にはほとんど影響しません。これにより、大規模なデータセットを処理するときに、バイナリ検索などのアルゴリズムが非常に効率的になります。
アルゴリズム:問題を解決するための手順/式
アルゴリズムをどのように分析し、アルゴリズムを相互に比較することができますか?
例:あなたと友人は、0からNまでの数を合計する関数を作成するよう求められます。あなたはf(x)を思い付き、あなたの友人はg(x)を思い付きます。両方の関数の結果は同じですが、アルゴリズムが異なります。アルゴリズムの効率を客観的に比較するために、Big-O表記法を使用しています。
ビッグO記法:説明ランタイムは、任意の大き得る入力として入力に対して大きくなりますどのように迅速に。
3つの重要なポイント:
スペースの複雑さ:時間の複雑さの他に、スペースの複雑さ(アルゴリズムが使用するメモリ/スペースの量)も考慮します。操作時間をチェックする代わりに、メモリ割り当てのサイズをチェックします。