プログラムが正しく動作していても、扱うデータ量が増えた途端に処理が極端に遅くなることがあります。
その原因を予測し、より効率的なアルゴリズムを選ぶための共通言語が、計算量のオーダーです。
とくにビッグO記法は処理時間の増え方を大まかに比較する考え方として、プログラミング学習から実務の設計まで幅広く使われています。
この記事では、計算量の意味、O記法の読み方、求め方、代表例、計算量理論とのつながりを順に整理します。
計算量のオーダーの意味

それではまず計算量のオーダーについて解説していきます。
入力サイズに対する処理の増え方
計算量とは、入力データの量が増えたときに、プログラムの実行時間や必要なメモリがどの程度増えるかを表す目安です。
たとえば要素数が10件から100件になったとき、処理回数が10倍程度になるのか、100倍程度になるのかで、実用性は大きく変わります。
ここでいう入力サイズは、一般にnという文字で表します。
配列の要素数、文字列の長さ、グラフの頂点数、検索対象のレコード数などがnに当たります。
計算量は秒単位の実測値ではなく、データ量に応じた伸び方を見る尺度です。
そのため、パソコンの性能やプログラミング言語が違っても、アルゴリズム同士の特徴を比較しやすくなります。
時間計算量と空間計算量
計算量には主に時間計算量と空間計算量があります。
時間計算量は処理に必要な計算回数の増加傾向を示し、空間計算量は処理中に追加で必要となるメモリ量の増加傾向を示します。
高速な処理を目指してデータを多く保持すれば、時間は短縮できてもメモリ消費が増える場合があります。
逆にメモリ使用量を抑える設計では、同じ計算を何度も行うために時間がかかることもあるでしょう。
実務では時間計算量だけで判断せず、メモリ容量、通信回数、データベースへのアクセス、保守性まで含めて設計することが重要です。
計算量のオーダーは万能な採点表ではなく、性能上のリスクを早い段階で見つけるための地図と考えると理解しやすくなります。
オーダー表記が必要になる場面
小さなデータだけを扱うプログラムでは、処理速度の差がほとんど見えないこともあります。
しかし利用者数、商品数、ログ量、画像数が増えれば、わずかな処理の違いが大きな待ち時間につながります。
たとえば数百件では問題のない二重ループでも、数百万件を対象にすると現実的な時間で終わらないかもしれません。
設計レビュー、技術面接、競技プログラミング、データ分析基盤の構築では、規模が拡大した後も耐えられるかを判断する材料としてオーダーが役立ちます。
まずは処理の回数がnに対してどのように増えるかを意識するだけでも、コードの見え方が変わってくるでしょう。
ビッグO記法の基本
続いてはビッグO記法の基本を確認していきます。
最悪計算量を示す表し方
ビッグO記法は、大きな入力に対して処理量がどの程度の上限で増えるかを表す表記です。
O記法とも呼ばれ、O(n)、O(n²)、O(log n)のように書きます。
厳密な数学では上界を示すための記法ですが、プログラミングの説明では最悪ケースの時間計算量を表す用途で使われることが一般的です。
たとえば配列を先頭から順番に探す線形探索は、目的の値が最後にある場合や存在しない場合に、最大でn回確認します。
この処理はO(n)です。
ビッグO記法では細かな実行時間より、入力が大きくなったときの支配的な増え方に注目します。
定数項と低次項の扱い
ある処理回数が3n+20回であったとしても、ビッグO記法ではO(n)と表します。
nが十分に大きくなると、固定値の20や係数の3より、nに比例して増える部分の影響が中心になるためです。
同じ理由でn²+100n+50はO(n²)となります。
3n+20はO(n)です。
n²+100n+50はO(n²)です。
2n³+nはO(n³)です。
これは定数を無視して雑に考えるためではありません。
入力規模が拡大したときにどの項が最も大きく影響するかを、意図的に取り出しているのです。
Big ThetaとBig Omegaとの違い
計算量理論では、ビッグO以外にΘ記法とΩ記法も使われます。
Θ記法は増加の上限と下限が同じ程度であることを示し、漸近的な増え方が確定している場面で用いられます。
Ω記法は少なくともどれだけの処理が必要かという下限を示す記法です。
たとえば比較にもとづくソートでは、一般にΩ(n log n)より速くできないことが知られています。
一方で実務の会話では、厳密にはΘ(n)といえる場合にもO(n)と説明されることが少なくありません。
用語の正確さを求める資料では区別しつつ、日常の設計ではO記法を性能の伸び方を示す共通表現として使うとよいでしょう。
代表的な計算量の比較
続いては代表的な計算量の比較を確認していきます。
定数時間と対数時間
O(1)は定数時間と呼ばれ、入力サイズが増えても処理回数がほぼ増えない状態です。
配列の添字を指定して要素を取得する処理は、一般的にO(1)として扱われます。
O(log n)は対数時間であり、対象を半分ずつ絞り込む二分探索が代表例です。
100万件の並び替え済みデータでも、二分探索なら確認回数はおよそ20回程度に抑えられます。
ただし二分探索には、データがあらかじめ整列していることが必要です。
検索だけを見るのではなく、整列に必要なコストも含めて考える視点が欠かせません。
線形時間と線形対数時間
O(n)は線形時間で、データを一度ずつ確認する処理に多く見られます。
配列の合計値、最大値、件数を求める走査は、通常O(n)です。
O(n log n)は線形対数時間で、効率のよいソートアルゴリズムによく現れます。
マージソートやヒープソートは代表例であり、大量データを扱う際にも比較的実用的な計算量です。
ソート後に何度も検索する処理では、最初にO(n log n)を支払う価値が生まれることがあります。
単発の検索なのか、繰り返し使うデータなのかで、最適な選択は変わります。
二乗時間と指数時間
O(n²)は二乗時間で、全要素の組み合わせを比較する二重ループなどで発生します。
数十件程度なら問題が見えなくても、件数が10倍になると処理量は約100倍になるため注意が必要です。
O(2ⁿ)やO(n!)は指数時間や階乗時間と呼ばれ、組み合わせを総当たりで列挙する処理に現れます。
| 計算量 | 代表的な処理 | 入力増加時の特徴 |
|---|---|---|
| O(1) | 配列の添字アクセス | ほぼ増えません |
| O(log n) | 二分探索 | 非常に緩やかに増えます |
| O(n) | 全件走査 | 件数に比例します |
| O(n log n) | 高速な比較ソート | 大規模でも扱いやすい傾向です |
| O(n²) | 全ペア比較 | 件数増加の影響が大きめです |
| O(2ⁿ) | 部分集合の総当たり | 少しの増加でも急激に重くなります |
指数時間のアルゴリズムが常に悪いわけではありません。
対象が小さい、厳密解が必要、枝刈りで候補を大幅に減らせるといった条件では、有効に働く場合もあります。
計算量の求め方
続いては計算量の求め方を確認していきます。
基本操作とループ回数
計算量を求める際は、まず繰り返される基本操作を見つけます。
比較、加算、代入、配列へのアクセスなど、処理の中心となる操作が何回行われるかを数えます。
単純なforループがn回繰り返され、その中が一定回数の処理だけなら計算量はO(n)です。
iを0からn未満まで1ずつ増やして値を出力する処理では、出力操作がn回行われます。
したがって時間計算量はO(n)です。
ループの中身に別のループがあり、それぞれがn回ずつ動くなら、基本操作はおよそn×n回になります。
この場合はO(n²)です。
入れ子のループは足し算ではなく掛け算になることが多いため、最初に構造を確認すると判断しやすくなります。
逐次処理と分岐処理
処理が順番に続く場合、計算量は通常足し合わせます。
たとえばO(n)の走査の後にO(n²)の比較を行うなら、全体はO(n+n²)です。
ビッグO記法では支配的な項を残すため、結果はO(n²)となります。
if文のような分岐では、最悪計算量を考えるなら最も重い経路を採用します。
片方がO(n)、もう片方がO(log n)なら、最悪ケースはO(n)です。
分岐の発生確率が低くても、利用者の入力や外部データによって重い経路が起こり得るなら、最悪ケースを確認しておく必要があります。
一方で平均的な応答時間が重要なサービスでは、平均計算量や実測値も併せて検討することが現実的です。
再帰処理と漸化式
再帰処理では、関数が何回呼び出されるかを漸化式として考えます。
二分探索は毎回探索範囲を半分にするため、T(n)=T(n/2)+O(1)と表せます。
これをたどると、入力が1になるまで半分にし続けるため、計算量はO(log n)です。
マージソートでは、配列を半分に分割する再帰と、全要素をまとめる処理が各段階で必要になります。
マージソートの考え方はT(n)=2T(n/2)+O(n)です。
分割の深さはlog nであり、各段階の併合量は合計でn程度です。
そのため全体の時間計算量はO(n log n)になります。
再帰は見た目だけでは回数を把握しにくいため、1回の呼び出しで問題がどれだけ小さくなるか、同時に何個の呼び出しが生まれるかを分けて考えるとよいでしょう。
実装時に見る計算量
続いては実装時に見る計算量を確認していきます。
データ構造による違い
同じ検索という目的でも、配列、連結リスト、ハッシュテーブル、平衡二分探索木では計算量が異なります。
配列の先頭から探すならO(n)ですが、ハッシュテーブルでキーを検索する場合は平均的にO(1)で取得できることがあります。
ただしハッシュ衝突、再ハッシュ、メモリ使用量、順序の必要性なども考慮しなければなりません。
平衡二分探索木では、検索、挿入、削除をO(log n)で扱えるため、順序を保ちながら更新する用途に向いています。
アルゴリズムだけでなく、選ぶデータ構造が計算量を決める場面は非常に多いものです。
ネストと全件検索の注意点
実務で見落とされやすいのは、ループの中で検索やデータベース問い合わせを繰り返す処理です。
顧客一覧を走査しながら、各顧客ごとに注文一覧を検索する構造では、件数に応じて大きな負荷が発生する可能性があります。
メモリ上の処理ではO(n²)に見えるものが、通信を伴うと待ち時間や接続数の問題まで引き起こします。
あらかじめキーごとの辞書を作る、一括取得して結合する、インデックスを活用するといった改善が有効でしょう。
処理が遅いと感じたら、内側のループや繰り返し実行される外部アクセスを最初に疑うと、改善箇所を見つけやすくなります。
ただし最適化を急ぎすぎて複雑なコードにすると、保守性が下がることもあります。
実測値とオーダーの使い分け
O(1)と書かれていても、内部処理の定数が非常に大きければ、小規模データではO(n)の処理より遅い場合があります。
またキャッシュ効率、CPU、メモリ配置、コンパイラの最適化、ネットワーク遅延は、ビッグO記法だけでは分かりません。
そのため改善の候補を絞る段階では計算量を使い、最終的な判断ではプロファイラや負荷試験で測定する流れが有効です。
理論上の増加傾向と実際のボトルネックは、両方を確認して初めて設計に活かせます。
想定する最大データ量、許容応答時間、同時利用者数を具体的に置くことで、必要な計算量の水準も判断しやすくなります。
計算量理論との関係
続いては計算量理論との関係を確認していきます。
効率よく解ける問題の分類
計算量理論は、問題を解くために必要な計算資源を数学的に研究する分野です。
アルゴリズムの速さだけでなく、そもそも問題が現実的な時間で解けるのかを考えます。
代表的な概念にPとNPがあります。
Pは、入力サイズに対して多項式時間で解ける問題の集まりです。
O(n)、O(n²)、O(n³)などは多項式時間に含まれます。
データ量によっては重くなるものの、指数時間と比べれば増加は緩やかで、大規模化に対応できる可能性を持つ計算量と考えられます。
NP問題と探索の難しさ
NPは、ある解が正しいかどうかを多項式時間で確認できる問題の集まりです。
巡回セールスマン問題、充足可能性問題、ナップサック問題などは、条件によって難しい計算問題として扱われます。
候補をすべて試せば解けるとしても、候補数が指数的に増えると、現実的な時間では処理できなくなります。
このような問題では、厳密解にこだわらず近似アルゴリズム、ヒューリスティック、動的計画法、乱択手法などを用いることがあります。
計算量を理解すると、なぜ完璧な最適化が難しいのか、なぜ近似解が選ばれるのかも説明しやすくなるでしょう。
現場の設計への活用
計算量理論の高度な証明を日常業務で行う機会は多くないかもしれません。
それでも、問題の規模と探索空間を意識する姿勢は、見積もりや設計判断に直結します。
たとえば全組み合わせの比較が必要に見える要件でも、条件を分割する、事前集計する、探索範囲を絞ることで、扱いやすい問題へ変換できる場合があります。
アルゴリズムを選ぶ前に、入力の上限や正確性の要求、許容できる処理時間を整理することが重要です。
問題そのものの形を変えることが、最も大きな計算量改善につながることも珍しくありません。
計算量のオーダーのまとめ
計算量のオーダーは、データ量の増加に対して処理時間やメモリ使用量がどのように変化するかを捉えるための考え方です。
ビッグO記法では、定数項や低次項を省き、O(1)、O(log n)、O(n)、O(n log n)、O(n²)のように支配的な増加傾向を表します。
ループ、入れ子、分岐、再帰、データ構造を確認すれば、コードのおおまかな計算量を求められます。
とくに大量データを扱う場面では、二重ループや繰り返しの外部アクセスが性能低下の原因になりやすいため、早めの確認が大切です。
一方で計算量だけでは実際の速度を断定できません。
オーダーで設計上の方向性を定め、実測でボトルネックを確かめるという組み合わせが、無理のない性能改善につながるでしょう。