English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

nu

ゲスト
1 / ?
レッスン一覧に戻る

成長率、絶対的なコストではなく

ビッグオー:コストの成長速度を測定する

ビッグオー記法は、アルゴリズムのコストの成長率を測定します。絶対的なコストではなく。

ビッグオーが答える質問は:入力サイズNが2倍になると、作業も2倍になるのか?4倍になるのか?そのままか?

「O」は「大きさの次数」を表します。括弧内の式は成長曲線の形を記述します。

ビッグオー成長表:O(1)、O(N)、O(N²)のN=10、100、1,000での操作回数

主要な複雑度クラス:

- O(1) —定数: コストは増えません。例:配列のインデックスで値を検索する。配列が10個の項目でも1000万個でも、1回の検索は1回の検索です。

- O(N) —線形: コストは入力とともに成長します。例:リストの各項目を1回読む。リストを2倍にすると、読む回数も2倍になります。

- O(N²) —二次: コストは入力の二乗として成長します。例:各項目を他のすべての項目と比較する。Nを2倍にするとコストは4倍になります。

成長比較表:

NO(1)O(N)O(N²)
10110100
100110010,000
1,00011,0001,000,000

N=10では差は小さく見えます。N=1,000では差は巨大になります。

O(N)とO(N²)を比較する

上記の成長比較表を使用してください。

N=1,000では:O(N)は1,000操作が必要です。O(N²)は1,000,000操作が必要です。

N=1,000で、O(N²)はO(N)と比べて何倍の操作が必要ですか?計算過程を示してください。

コード構造が複雑性をどのように明かすか

コードからビッグオーを特定する方法

ビッグオーはコードの形に隠れています。4つのパターンがほとんどのケースをカバーします:

N個の項目上の単一ループ:O(N)

# O(N):リストの1回の走査
for item in list_of_n_items:
    process(item)

N個の項目上のネストされたループ:O(N²)

# O(N²):すべての項目がすべての他の項目とペアになります
for item_a in list_of_n_items:
    for item_b in list_of_n_items:
        compare(item_a, item_b)

定時間検索:O(1)

配列インデックスアクセス、ハッシュテーブル検索—サイズに関わらず1ステップ。

分割統治(各ステップで問題を半分に分割):O(log N)

二分探索:各チェックで残りの項目が半分になります。

---

隠れたO(N²):ループ内のリスト

# これはO(N)のように見えますが、実際にはO(N²)です
visited = []
for item in list_of_n_items:
    if item not in visited:   # visitedのすべてをスキャン:O(N)
        visited.append(item)  # 全体のループ:O(N²)

if item not in visited行は、各反復でvisitedのすべての要素をスキャンします。リストスキャンにはO(N)かかります。N回実行されるループが、各実行でO(N)の作業を行う:O(N) × O(N) = O(N²)

このパターンは1,000以上のオープンソースプロジェクトに現れます。修正は1文字で済みます:visited = []visited = set()に置き換えます。セットメンバーシップテストはO(1)で、O(N)ではありません。1つの変更です。同じ結果。N=1,000で1,000倍高速。

このコードを分類して修正する

このコードを読んでください:

result = []
for name in student_names:
    if name not in result:
        result.append(name)
このコードのビッグオー複雑度は何ですか?`if name not in result`行が高コストになる理由を説明してください。その後、コードをO(N)にしてください。

理論と実践が出会う

野生のビッグオー

ビッグオーは単なる理論ではありません。同じタスクで、数秒で完了するプログラムと17分かかるプログラムを分けます。

実際の例:ビルドシステムの依存関係サイクル検出。

ソフトウェアをインストールするとき、コンピュータは依存関係のグラフを解決します:パッケージAはB が必要、BはCが必要、など。ビルドシステムはこのグラフをサイクルについてチェックします。

O(N²)サイクル検出アルゴリズムはN=100パッケージで正常に動作します。N=50,000パッケージ(実際のプロジェクト)で:17分かかります。O(N)修正:10秒

この正確な欠陥はGHC(Haskellコンパイラ)、pip(Pythonパッケージマネージャー)、Maven(Javaビルドシステム)、Cargo(Rustパッケージマネージャー)、&1,000以上の他のプロジェクトに存在します。

作者が無注意だからではありません。N が小さい時にvisited = []が書かれました。コードが石化しました。Nが成長しました。ビルドが半時間かかるまで誰も気づきませんでした。

ビッグオーは、何が起こったかを名前を付け、修正できる語彙です。

---

もっと深く学びたいですか? unhammmingコースはビッグオーの完全な章、MOAD-0001(1,000以上のオープンソースプロジェクトで見つかった実際のO(N²)欠陥)、&パターンに名前を付けることがどこでもそれを見つけることができる理由が含まれています。失われた章:アルゴリズム複雑度を参照してください。

ビルド時間を予測する

ビルドシステムはO(N²)サイクル検出を使用しています。

測定されたビルド時間:

- N=100パッケージ:0.01秒

- N=1,000パッケージ:1秒

O(N²)の場合:時間は(N_new / N_old)²としてスケーリングします。

O(N)の場合:時間は(N_new / N_old)としてスケーリングします。

これらの公式と測定データを使用して:(A)N=10,000で、O(N²)バージョンはどのくらい時間がかかりますか?(B)O(N)修正後、N=1,000をベースラインとして、O(N)バージョンはN=10,000でどのくらい時間がかかりますか?両方の計算過程を示してください。