ビット演算で最大公約数(GCD)を計算する話:Pythonで学ぶ「Steinのアルゴリズム」

こんにちは、テック好きの皆さん!今日は、ちょっと面白いアルゴリズムの話をしましょう。
最大公約数(GCD)の計算といえば、みんながよく知る「ユークリッドのアルゴリズム」が定番ですよね。でも、もっと効率的でクールな方法があるんです。それが「Steinのアルゴリズム(ビットGCDアルゴリズム)」。これ、割り算を使わずにビット演算で最大公約数を求める方法なんです。

smart kid

GCDをビットで求める?何が嬉しいの?

通常のユークリッド法では、割り算や剰余(mod)演算が中心になります。しかし、割り算は計算コストが高いことが知られています。特に低レベルのシステムや、数が巨大な場合は割り算を避けたいところ。

そこで登場するのが、ビット演算を使ったGCD計算。ビット演算は軽量で、プロセッサが非常に効率よく処理できるので、速度面でメリットが大きいんです。

さらに、アルゴリズムとしての美しさもポイント。ビット演算を駆使して数学的な問題を解くのは、プログラマにとってちょっとしたロマンですよね。


Steinのアルゴリズムとは?

Steinのアルゴリズム(またはビットGCDアルゴリズム)は、以下のアイデアに基づいています。

  1. 偶数の共通因数を取り除く
    偶数同士のGCDは必ず2で割れるので、両方の数を2で割り、共通因数「2」を数えておきます。
  2. 奇数に揃える
    片方が偶数の場合、偶数を2で割って奇数にします。
  3. 引き算で差を取る
    奇数同士の場合、大きい方から小さい方を引きます。この操作を繰り返して、値を徐々に小さくしていきます。
  4. 2の因数を元に戻す
    最後に、最初に取り除いた「2」をGCDに掛け算して答えを出します。

これを繰り返すことで、割り算なしでGCDを求めることができるのです。


Python実装

では、このアルゴリズムをPythonで実装してみましょう。

def gcd_stein(a: int, b: int) -> int:
    # ベースケース:どちらかがゼロならもう片方がGCD
    if a == 0:
        return b
    if b == 0:
        return a

    # ステップ1: 共通因数2をカウント
    shift = 0
    while ((a | b) & 1) == 0:  # a と b が両方偶数の間
        a >>= 1
        b >>= 1
        shift += 1

    # ステップ2: a を奇数にする
    while (a & 1) == 0:
        a >>= 1

    # ステップ3: メインループ
    while b != 0:
        # b を奇数にする
        while (b & 1) == 0:
            b >>= 1

        # 大小比較して差を計算
        if a > b:
            a, b = b, a  # a と b をスワップ
        b = b - a  # b を更新

    # ステップ4: 共通因数2を元に戻す
    return a << shift

実行例

アルゴリズムがどう動くのか、いくつかの例を試してみましょう。

print(gcd_stein(48, 18))  # 出力: 6
print(gcd_stein(101, 103))  # 出力: 1
print(gcd_stein(56, 98))  # 出力: 14

コードの解説

偶数の共通因数をカウント

while ((a | b) & 1) == 0:
    a >>= 1
    b >>= 1
    shift += 1

ここで、両方の値が偶数である間は2で割り続けます。最終的に取り除いた2の個数は shift に保存。

奇数化

while (a & 1) == 0:
    a >>= 1

a が偶数なら奇数になるまで右シフト。

大小比較と引き算

if a > b:
    a, b = b, a
b = b - a

abを比較して引き算。これを繰り返します。

結果を復元

return a << shift

最後に、最初に取り除いた2を掛けて(左シフトして)答えを出します。


    なぜビット演算で速くなる?

    • 割り算を使わない:割り算はCPUにとって重い処理。
    • シフト演算は高速:>><< はプロセッサで効率よく実行される。
    • 繰り返しの計算がシンプル:引き算とビット操作の組み合わせだけで計算を進められる。

    特に、数値が非常に大きい場合や、計算リソースが限られた環境(埋め込みシステムなど)では、この効率性が大きな差になります。


    まとめ:ビットで数学を操る楽しさ

    Steinのアルゴリズムは、ただ効率的なだけでなく、プログラマにとって「美しい」と感じられるアルゴリズムの1つです。通常のGCD計算よりも高速でありながら、割り算を一切使わないという発想の転換が素晴らしい。

    皆さんもぜひ、自分のプロジェクトや勉強の一環で試してみてください。アルゴリズムを理解する楽しさと、ビット演算の魅力にハマること間違いなしです!

    上部へスクロール