どれくらい速い?ビッグO記法
1秒に10億ステップなら、100万個の値に対して n² の方法は約17分かかりますが、n log n の方法は0.02秒です。
正確な回数より増え方が大切
ここまでは、比較45回、交換4回、調べる回数20回のように、ステップを正確に数えてきました。正確な回数は小さな例では役に立ちますが、バブルソートが途中で止まるかどうか、ピボットをどう選ぶかといった細かい点に左右されます。入力が大きくなると、ほかのすべてを圧倒するものが1つあります。nが大きくなるにつれて回数がどう増えるかです。ステップ数が n² のように増えるアルゴリズムは、どんなに念入りに調整しても、いずれは n log n のように増えるアルゴリズムに負けます。
ビッグO記法は、その増え方を短く書く方法です。アルゴリズムが O(n²)(「オーダー n の2乗」と読みます)であると書くのは、nが大きいとき、ステップ数がある定数かける n² 以下になるという意味です。2つのルールを使えば簡単に扱えます。定数倍は捨てます。3n も n/2 もどちらも O(n) です。いちばん速く増える項だけを残します。選択ソートの比較回数 n²/2 − n/2 は O(n²) です。nが大きいと、ほとんどが n² の部分になるからです。MITの6.006の講義でも、データ構造やソートを漸近的な実行時間で比べるときに、同じ考え方を使っています。
よく出てくる増え方
- O(1)、定数時間。仕事量がnによらない。リストの最初の値や、位置がわかっている値を読むことなど。
- O(log n)、対数時間。1ステップごとに問題が半分になる。二分探索。100万個の値で約20ステップ。
- O(n)、線形時間。各値を1回ずつ扱う。線形探索、最大の値を見つけること、リストの合計を求めることなど。
- O(n log n)。マージソートと、平均の場合のクイックソート。比較によるソートで達成できる最善。
- O(n²)、2乗時間。すべての組を調べる。最悪の場合のバブルソート、選択ソート、挿入ソート、または入れ子になった2つのループ。
- O(2ⁿ)、指数時間。値が1つ増えるごとに仕事量が2倍になる。n個のものの部分集合をすべて試すことや、ハノイの塔でn枚の円盤を動かすことなど。
ドラッグで回転、スクロールまたはピンチで拡大・縮小。
数を秒になおすと
コンピューターが1秒に10億(10⁹)回の基本ステップを行うとしましょう。プロセッサのコア1つとして手ごろな概数です。100万個の値に対して、それぞれの増え方が何を意味するかを見てみます。O(log n) は約20ステップで、1マイクロ秒よりずっと短い時間です。O(n) は100万ステップで、1ミリ秒です。O(n log n) は約2,000万(20 million)ステップで、0.02秒です。O(n²) は 10¹² ステップで、1,000秒、約17分です。O(2ⁿ) は終わりません。2の100万乗は300,000桁を超える数です。アルゴリズムを正しく選ぶことが、たいてい速いコンピューターを買うよりずっと大切なのはこのためです。10倍速い機械なら17分は1.7分になりますが、O(n²) から O(n log n) に切りかえれば50分の1秒になります。
| 増え方 | n = 1,000,000 のときのステップ数 | 1秒に 10⁹ ステップのときの時間 |
|---|---|---|
| O(1) | 1 | 約1ナノ秒 |
| O(log n) | 約20回 | 約20ナノ秒 |
| O(n) | 1,000,000 | 1ミリ秒 |
| O(n log n) | 約20,000,000 | 約0.02秒 |
| O(n²) | 1,000,000,000,000 | 約17分 |
| O(2ⁿ) | 300,000桁を超える | 終わらない |
コードからビッグOを読み取る
増え方は、コードの形から直接読み取れることがよくあります。リストを1回たどるループは O(n) です。どちらもリスト全体をたどる2つのループが入れ子になっていれば O(n²) です。外側のn回それぞれで、内側のループが最大n回まわるからです。while (n > 1) n = Math.floor(n / 2) のように毎回数を半分にするループは O(log n) で、1,000から始めると9回、100万からなら19回まわります。順に続く手順は足し合わされ、大きいほうの項が勝ちます。O(n) のループの後に O(n²) のループが続けば、全体は O(n²) です。
// O(n): 1回たどる
let total = 0;
for (const x of list) total += x;
// O(n²): すべての組
for (let i = 0; i < list.length; i++)
for (let j = i + 1; j < list.length; j++)
if (list[i] === list[j]) console.log("duplicate", list[i]);
// O(log n): 半分にする
let k = list.length, rounds = 0;
while (k > 1) { k = Math.floor(k / 2); rounds++; }最善、平均、最悪の場合
1つのアルゴリズムでも、入力によってふるまいが大きく違うことがあります。挿入ソートは、すでに整列したリストでは O(n)、逆順のリストでは O(n²) です。クイックソートは平均で O(n log n)、いつも悪いピボットを選ぶと O(n²) です。どの場合かを言わずにビッグOを1つだけ挙げるときは、ふつう最悪の場合を指します。それが保証になるからです。アルゴリズムはそれより悪くなることはありません。ビッグOには省かれていることもあります。定数倍を無視するので、小さな入力では、手順がとても簡単な O(n²) の方法が O(n log n) の方法に勝つことがあります。実際の多くのソート処理が、リストの短い部分では挿入ソートに切りかえるのはそのためです。
| ビッグO | 定数倍を無視した、nに対するステップ数の増え方 |
| O(1) | 定数時間:どんなnでも同じ仕事量 |
| O(log n) | 対数時間:二分探索のように、1ステップごとに半分にする |
| O(n) | 線形時間:入力を1回たどる |
| O(n log n) | マージソート。比較によるソートの最善 |
| O(n²) | 2乗時間:すべての組、入れ子の2つのループ |
| O(2ⁿ) | 指数時間:1つ増えるごとに2倍になる |
| 最悪の場合 | 保証:アルゴリズムがこれより多くの仕事をすることはない |
- 3n² + 5n + 2 をビッグO記法で表すとどうなりますか?
- O(n)
- O(n²)
- O(3n²)
- O(n² + n)
- それぞれn個の値をたどる2つのループが入れ子になっています。ビッグOは?
- O(n)
- O(2n)
- O(n²)
- O(log n)
- 二分探索は
- O(1)
- O(log n)
- O(n)
- O(n log n)
- O(n²) の方法で1,000個の値に2秒かかります。10,000個の値ではおよそ何秒かかりますか?
- O(n) の方法で1,000個の値に2秒かかります。10,000個の値ではおよそ何秒かかりますか?
- 1秒に 10⁹ ステップのとき、10¹² ステップ(100万個の値に対する n² の方法)には何秒かかりますか?
- nが64から始まるとき、
while (n > 1) n = Math.floor(n / 2)は何回まわりますか? - あるプログラムは O(n) のループを実行し、次に O(n²) のループを実行します。全体では
- O(n)
- O(n²)
- O(n³)
- O(n + 2)
- すでに整列したリストに対する挿入ソートは
- O(1)
- O(n)
- O(n log n)
- O(n²)
- 小さな入力では、O(n²) の方法が O(n log n) の方法に勝つことがあるのはなぜですか?
- ビッグOは定数倍を無視し、簡単な方法のほうが1ステップあたりの仕事が少ないことがあるから
- nが小さいとビッグOはまちがっているから
- どんなnでも n² は n log n より小さいから
- 小さな入力はいつも整列しているから
- マージソートの追加メモリは
- O(1)
- O(log n)
- O(n)
- O(n²)
- これらの増え方を、増え方のおそいものから速いものへ順に並べましょう。
- ビッグOは、定数倍や小さな項を無視して、nに対してステップ数がどう増えるかを表します。
- よく出てくる増え方:1、log n、n、n log n、n²、2ⁿ。
- 1秒に 10⁹ ステップで100万個の値なら、n log n は約0.02秒、n² は約17分。
- ループ1つは O(n)、入れ子の2つのループは O(n²)、半分にしていくのは O(log n)。
- ビッグOを1つだけ挙げるときは、ふつう最悪の場合を指す。
このコースの出典 (11)
- MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
- lecture notes 1 Introduction, 3 Sorting, 9 Breadth-First Search, 10 Depth-First Search (ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes) and Fall 2011 lecture 13 BFS notes (ocw.mit.edu/courses/6-006-introduction-to-algorithms-fall-2011)
- Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (MIT Press)
- with the CLRS-based Khan Academy algorithms unit by Thomas Cormen and Devin Balkcom of Dartmouth (khanacademy.org/computing/computer-science/algorithms: binary search, selection sort analysis, merge sort, quicksort overview and analysis)
- MacTutor History of Mathematics, Al-Khwarizmi (mathshistory.st-andrews.ac.uk/Biographies/Al-Khwarizmi: word algorithm from the Latin title Algoritmi de numero Indorum)
- D. E. Joyce, Euclid's Elements Book VII Propositions 1 and 2 (mathcs.clarku.edu/~djoyce/elements/bookVII)
- Wolfram MathWorld, Merge Sort (mathworld.wolfram.com/MergeSort.html: proposed by John von Neumann in 1945, after Knuth 1998 p. 159)
- Stanford CS "Tony Hoare: Quicksort" (cs.stanford.edu/people/eroberts/courses/soco/projects/2008-09/tony-hoare/quicksort.html: devised while a student at Moscow State University for Russian-English machine translation with a tape dictionary, programmed in Algol 60 and published 1961; the year 1959 from the Wikipedia Quicksort article and its cited sources)
- Bodleian Libraries, Ada Lovelace (blogs.bodleian.ox.ac.uk/adalovelace: 1843 paper, Note G Bernoulli numbers)
- Fourmilab text of Menabrea's Sketch with Lovelace's notes (fourmilab.ch/babbage/sketch.html)
- Seven Bridges of Konigsberg (Euler 1736) and Tower of Hanoi (Edouard Lucas, 1883) checked against the Wikipedia articles and their cited sources.
確認日: 2026-10-17