最終更新
リンドくん
先生、「Big-O記法」とか「計算量」っていう言葉をよく聞くんですけど、これって何なんですか?難しそうで...
たなべ
Big-O記法や計算量は「プログラムがどれくらい速いか」を表す物差しのようなものなんだよ。
料理で例えると、「何人分の料理を作るのにどれくらい時間がかかるか」を予測する方法みたいなものかな。
リンドくん
なるほど!でも、なんでそんなものが必要なんですか?
たなべ
それはね、同じ結果を出すプログラムでも、書き方によって速度が全然違うからなんだ。
10件のデータなら気にならなくても、100万件になったら何時間もかかる...なんてことも珍しくないんだよ。今日はそのあたりをしっかり理解していこう!
プログラミングを学んでいくと、「動くコードを書く」だけでなく「効率的なコードを書く」ことの重要性に気づく瞬間が訪れます。
その効率性を測る基準となるのがアルゴリズムの計算量であり、それを表現する方法がBig-O記法です。
本記事では、プログラミング初心者の方でも理解できるよう、Big-O記法とアルゴリズム計算量の基礎を丁寧に解説していきます。
この知識は、単なる理論ではなく、実際のプログラミングにおいて「なぜこのコードは遅いのか」「どう改善すれば速くなるのか」を判断する力につながります。
HackATAは、IT技術を習得する人のために広く開かれたオンラインコミュニティです。 現在、無料コミュニティとして開放していますので、ご気軽に参加してください。
✓ 再立ち上げ
✓ コミュニティの方向性について意見募集中
リンドくん
「計算量」って、具体的に何を計算しているんですか?
たなべ
それはね、データの量が増えたときに、処理時間や使うメモリがどれくらい増えるかを表しているんだ。
例えば、100人の名簿から特定の人を探すのと、10万人の名簿から探すのでは、時間のかかり方が全然違うよね?
アルゴリズムの計算量とは、プログラムが問題を解くために必要なリソース(時間やメモリ)の量を表す指標です。主に以下の2種類があります。
一般的に「計算量」といえば時間計算量を指すことが多く、本記事でも主に時間計算量について解説していきます。
実際のソフトウェア開発では、以下のような場面で計算量の理解が重要になります。
例えば、10件のデータを扱うプログラムでは、どんな書き方をしても一瞬で処理が終わるため違いが分かりません。しかし、100万件のデータになると、効率の悪いアルゴリズムでは数時間かかることもあるのです。
計算量を測る際、私たちは入力サイズ(データの量)が増えたときに、処理時間がどう変化するかに注目します。
具体的には以下のような視点で考えます。
この「データ量と処理時間の関係性」を数学的に表現したものがBig-O記法なのです。
リンドくん
Big-O記法って、O(n)とかO(1)とか見るんですけど、あれって何を表しているんですか?
たなべ
あぁ、それね!Oは「Order(オーダー)」の頭文字で、カッコの中の文字が「データ量が増えたときの処理時間の増え方」を表しているんだ。nはデータの個数を表す変数だよ。
Big-O記法(Big O Notation)は、アルゴリズムの効率を表現するための数学的な記法です。
最悪の場合の計算量を表現することで、そのアルゴリズムのパフォーマンスを客観的に評価できます。
書き方は以下のような形式です。
この「関数」の部分に、入力サイズnに対する処理量の増加率を表す式が入ります。
よく使われる計算量を、速い順に並べると以下のようになります。
下に行くほど、データ量が増えたときに処理時間が急激に増加します。実用的なプログラムでは、できるだけ上の方(速い計算量)を目指すことが重要です。
それぞれの記法が実際に何を意味するのか、簡単に説明します。
この記法を理解することで、「このアルゴリズムはデータ量が増えるとどうなるか」を予測できるようになります。
リンドくん
O(1)って、データが増えても処理時間が変わらないってことですか?すごいですね!
たなべ
そうなんだ!例えば、配列の最初の要素を取り出すとか、変数に値を代入するといった処理がこれに当たるよ。何回実行しても同じ時間で終わるんだ。
O(1)は定数時間と呼ばれ、入力サイズに関係なく常に一定の時間で処理が完了します。これは最も効率的な計算量です。
具体的には以下のような処理がO(1)になります。
このコードは、配列に10個の要素があっても、100万個の要素があっても、同じ速度で最初の要素を取得できます。なぜなら、配列の先頭位置はメモリ上で決まっているため、すぐにアクセスできるからです。
配列のようなインデックスでアクセスできるデータ構造では、コンピュータは直接その位置に飛んでいけます。これは本の目次から特定のページを開くのと似ています。何ページあっても、ページ番号さえわかれば一瞬で開けますよね。
リンドくん
O(n)は、データがn個あればn回処理するってことですか?
たなべ
その通り!例えば、配列のすべての要素を順番に見ていくような処理がこれだね。データが2倍になれば処理時間も2倍になるんだ。
O(n)は線形時間と呼ばれ、入力サイズに比例して処理時間が増加します。プログラミングで最もよく見かける計算量の一つです。
以下のような処理がO(n)になります。
このコードでは、配列の要素数が増えれば増えるほど、ループの回数も比例して増加します。10個なら10回、100万個なら100万回のループが必要です。
線形探索は、データ構造の中でも基本的な操作です。
この線形探索では、最悪の場合(探している値が配列の最後にある、または存在しない場合)、すべての要素をチェックする必要があります。
リンドくん
O(n²)って、すごく遅そうですね...
たなべ
そうなんだ。ループの中にループがあると、この計算量になることが多いんだよ。
10個のデータなら100回、100個なら10,000回の処理になっちゃうからね。大量のデータには向かないんだ。
O(n²)は二乗時間と呼ばれ、入力サイズの二乗に比例して処理時間が増加します。これは初心者が書きがちな、効率の悪いコードの典型例です。
以下のような処理がO(n²)になります。
このコードでは、外側のループがn回、その内側でさらにn回ループするため、合計でn×n回の処理が発生します。
データ量が増えると、処理時間が急激に増加します。
このように、少しデータが増えるだけで処理時間が爆発的に増えてしまうのが、O(n²)の大きな問題点です。実際のアプリケーション開発では、できるだけO(n²)のアルゴリズムを避けることが重要になります。
先ほどの重複検出は、集合(Set)を使うことでO(n)に改善できます。
このように、適切なデータ構造を選ぶことで、計算量を大幅に改善できることがあります。
リンドくん
O(log n)って、どういう意味ですか?logって数学で習ったような...
たなべ
そう、対数だね!簡単に言うと、データを半分ずつに分けていくような処理がこれに当たるんだ。1,000個のデータでも10回程度の処理で終わる、とても効率的な計算量なんだよ。
O(log n)は対数時間と呼ばれ、データ量が増えても処理時間がゆっくりとしか増えない、非常に効率的な計算量です。
代表的な例は二分探索(バイナリサーチ)です。これは、ソート済みの配列から値を探す際に、毎回対象範囲を半分に絞っていく手法です。
辞書で単語を調べるときのことを思い出してください。
こうすることで、数万語ある辞書でも数回のチェックで目的の単語を見つけられます。これがO(log n)の考え方です。
このコードでは、10個の要素がある配列でも、最大で4回程度のチェックで目的の値を見つけられます(log₂(10) ≈ 3.3)。
データ量と必要な処理回数の関係を見てみましょう。
100万個のデータでも20回程度の処理で済むというのは驚異的な効率です。これがO(log n)の大きな強みです。
リンドくん
結局、どの計算量を目指せばいいんでしょうか?
たなべ
それは扱うデータの量と、求められる速度によって変わってくるんだ。
少量のデータならO(n²)でも問題ないけど、大量のデータを扱うならO(n)やO(log n)を目指すべきだね。
データ量による処理回数の違いを比較してみましょう。
| データ量 | O(1) | O(log n) | O(n) | O(n log n) | O(n²) |
|---|---|---|---|---|---|
| 10 | 1 | 3 | 10 | 33 | 100 |
| 100 | 1 | 7 | 100 | 664 | 10,000 |
| 1,000 | 1 | 10 | 1,000 | 9,966 | 1,000,000 |
| 10,000 | 1 | 13 | 10,000 | 132,877 | 100,000,000 |
この表を見ると、データ量が増えるにつれて、計算量の違いが処理時間に大きく影響することがわかります。
効率の悪いコードを改善する典型的なパターンは以下です。
パターン① ループをなくす
パターン② 適切なデータ構造を使う
パターン③ ソート済みデータを活用
パターン④ 二重ループを避ける
リンドくん
普段からどんなことに気をつければ、効率的なコードが書けるようになりますか?
たなべ
いくつかポイントがあるんだけど、まずは「ループが何回回るか」を常に意識することかな。
あとは、適切なデータ構造を選ぶことも大事だよ。慣れてくると自然に効率的なコードが書けるようになるよ!
コードを書くとき、または読むときに、以下を意識しましょう。
用途に応じて最適なデータ構造は異なります。
同じ計算を何度も繰り返すのは無駄です。
不要な処理は避けましょう。
リンドくん
計算量のことがだいぶわかってきました!これからは効率的なコードを意識して書いてみます!
たなべ
その意気だね!最初から完璧を目指す必要はないよ。
まずは動くコードを書いて、それから改善していくというアプローチで大丈夫。計算量を意識することで、確実にエンジニアとしてのレベルが上がっていくはずだよ!
Big-O記法とアルゴリズムの計算量について、基本的な内容を解説してきました。この知識は、プログラミング初心者からプロのエンジニアへと成長する過程で、必ず身につけるべき重要なスキルです。
今回学んだ重要なポイントを振り返りましょう。
計算量の理解は、単なる理論知識ではありません。
実際のアプリケーション開発では、ユーザー体験を左右する重要な要素です。レスポンスの速いアプリケーションは、ユーザーからの評価も高くなります。
計算量を意識することは、最初は難しく感じるかもしれません。
しかし、コードを書くたびに「このループは何回回るかな?」「もっと効率的な方法はないかな?」と考える習慣をつけることで、自然と効率的なコードが書けるようになります。
プログラミングでは、「動くコード」から「良いコード」へ、そして「効率的なコード」へと成長していくことが大切です。