ユークリッドの互除法(GCD)

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

ユークリッドの互除法(GCD)

このレッスンで分かること

  • 4 ステップで 6 という答えに到達します
  • 最大公約数 (GCD, Greatest Common Divisor) は、2 つ以上の整数に共通する 最大の約数 のことです
  • ユークリッドの互除法は、次の漸化式で表されます

ユークリッドの互除法 とは

最大公約数を gcd(a, b) = gcd(b, a % b) の漸化式で求める。古典中の古典の再帰アルゴリズム。

最大公約数 (GCD, Greatest Common Divisor) は、2 つ以上の整数に共通する 最大の約数 のことです。gcd(12, 18) = 6gcd(7, 13) = 1gcd(100, 25) = 25 という具合。これを求める最古のアルゴリズムが ユークリッドの互除法 で、紀元前 300 年頃の数学者 ユークリッド が著書『原論』で記述したものです。

2300 年以上前から使われているアルゴリズムが、現代でも O(log min(a, b)) という最高クラスの計算量で動く。これがアルゴリズムの美しさだ。

互除法の漸化式

ユークリッドの互除法は、次の漸化式で表されます。

  • gcd(a, 0) = a (b が 0 になったら a が答え)
  • gcd(a, b) = gcd(b, a mod b) (b > 0 のとき)

この 2 行で完成、というシンプルさが特徴です。a mod ba を b で割った余りPython では a % b です。

なぜこれで GCD が求まる?

直感的な説明として、ab の共通の約数は、ba mod b の共通の約数とも一致します。たとえば gcd(48, 18) を計算してみます。

プレーンテキスト

gcd(48, 18) = gcd(18, 48 mod 18) = gcd(18, 12) gcd(18, 12) = gcd(12, 18 mod 12) = gcd(12, 6) gcd(12, 6) = gcd(6, 12 mod 6) = gcd(6, 0) gcd(6, 0) = 6

4 ステップで 6 という答えに到達します。素朴に「1 から順に割ってみる」方法だと最大 min(a, b) 回かかるところ、互除法では O(log min(a, b)) で済むのが強みです。

Mermaid で見る再帰の流れ

diagram (will load when visible)

図のポイント (テキスト併記)

  • b = 0 まで降りたら a を返し、そのまま 6 が戻ってきます

b = 0 まで降りたら a を返し、そのまま 6 が戻ってきます。階乗やフィボナッチと違い、掛け算や加算 が起きないので、戻り値はずっと同じです。

Python の実装

Python

def gcd(a, b): if b == 0: return a return gcd(b, a % b)

if b == 0: return a基底ケースgcd(b, a % b)再帰ケース。たった 3 行のコードで 最大公約数 が求まる、というのがユークリッドの互除法の魅力です。

JavaScript / Java での書き方

JavaScript

function gcd(a, b) { if (b === 0) return a; return gcd(b, a % b); }

Java

public class Solution { public static int gcd(int a, int b) { if (b == 0) return a; return gcd(b, a % b); } }

どの言語でも、% (剰余演算子) と if return の組み合わせで書けます。Python に至っては標準ライブラリ math.gcd(a, b) で同じ結果が返るので、本番では組み込み関数を使うのが普通です。

計算量

ユークリッドの互除法の計算量は O(log min(a, b)) です。ab がフィボナッチ数列の連続する 2 項のとき、最悪計算量になることが知られています (これも面白い性質)。

O(log n) で動くアルゴリズムは、n = 10^18 でも 60 回程度の計算で終わる。圧倒的に速い。

よくある間違い

  • if a == 0: return b を基底にしてしまう。これは別の書き方として正しくは動くが、本レッスンでは b == 0: return a で統一する。
  • gcd(a, b)a < b のときどうなる? → 心配無用。a % ba のままになるので、次のステップで gcd(b, a) と引数が入れ替わり、b > a の状態に補正される。
  • 負の数を渡された場合の挙動を考えていない。本レッスンでは a, b >= 1 を前提とする。

やってみよう

下の coding 課題で、gcd(a, b) をユークリッドの互除法で実装してください。テストでは gcd(12, 18) = 6 のような典型ケースに加え、gcd(7, 13) = 1 (互いに素) や gcd(100, 25) = 25 のような片方が倍数のケースもチェックします。for / while / 組み込み math.gcd は使わず、必ず再帰で書いてください。

ユークリッド互除法は RSA 暗号 モジュラ逆元 の計算など、暗号理論の基礎としても使われている。2300 年経っても現役、というのは驚異的だ。

最小公倍数 (LCM) との関係

最大公約数 (GCD) と 最小公倍数 (LCM) には、次の有名な関係があります。

  • lcm(a, b) = a * b / gcd(a, b)

この式を使えば、gcd を実装するだけで lcm も自動的に計算できる、ということです。たとえば lcm(12, 18) = 12 * 18 / 6 = 36 です。gcd がいかに基礎的なアルゴリズムか、よく分かる例です。

Python

def lcm(a, b): return a * b // gcd(a, b)

3 つ以上の GCD

gcd(a, b, c) のように 3 つ以上の数の最大公約数を求めたい場合は、gcd(gcd(a, b), c) のように 2 つずつまとめて計算すれば OK です。これは gcd結合則 を満たすことから保証されます。reduce 関数と組み合わせると、配列全体の gcd も 1 行で書けます。

Python

from functools import reduce def gcdAll(arr): return reduce(gcd, arr)

本レッスンの coding 課題では 2 引数版の gcd のみを扱いますが、関数を 1 つ実装するだけで、lcm配列の gcd まで広がる、というのが アルゴリズムの応用力 の面白さです。

よくある質問

Q. このトピックは実務でどう役立ちますか?

A. DB のクエリ最適化、API 設計、データ構造の選択など、設計判断の根拠になります。表面的にライブラリを使うだけでなく「なぜそれが速いのか」を理解できると、性能問題を未然に防げます。コーディング面接でも頻出のテーマです。

Q. 計算量はどう求めれば良いですか?

A. ループのネストごとに掛け算する、再帰なら漸化式から解く、というのが基本です。Big-O 表記は定数倍と低次の項を無視するため、n の指数(n²、n log n など)に注目してください。最悪・平均・最良の 3 つを意識すると説得力が増します。

Q. 覚えるべき定番アルゴリズムは何ですか?

A. 二分探索、クイックソート/マージソート、BFS/DFS、ダイクストラ、DP の基本問題(フィボナッチ・ナップサック)が必修です。これらを「白紙から書ける」状態にすると、応用問題が一気に解けるようになります。

次のレッスン

次は 第1章まとめクイズ — 再帰の基礎 で、最大公約数を gcd(a, b) = gcd(b, a % b) の漸化式で求める を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. ユークリッド互除法 の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. ユークリッド互除法 とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. 関数 gcd(a, b) を実装し、最大公約数を整数で返す
  2. for / while / 組み込み math.gcd を使わず、必ず再帰で実装する
  3. 基底ケースは b == 0 のとき a を返す

入出力例

test-cases.txt

gcd(12, 18)6 gcd(18, 12)6 gcd(7, 13)1 gcd(100, 25)25 gcd(48, 36)12 gcd(1, 1)1

ヒント

main.py
main.py
学習モード

メモ

ユークリッドの互除法(GCD)

⌘S で保存