C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++でラビン・ミラー素数判定テストを実装し、素数かどうかを判定する方法

ラビン・ミラー素数性テスト(Rabin–Miller Primality Test)は、与えられた整数が素数であるかどうかを判定するための確率的アルゴリズムです。フェルマーの素数判定法やソロベイ・シュトラッセン・テストと同様の形式を持ち、この手法の原型はロシアの数学者 M. M. Artjuhov によって考案されました。

このテストは、反復回数を増やすことで誤判定の確率を極めて低く抑えられるため、巨大な整数に対しても高速に素数判定が可能であり、暗号技術などの分野で広く活用されています。

アルゴリズム

本実装の中核となるのは、次の3つの関数です。

1. mulmod:モジュロ乗算

(a × b) mod m を、オーバーフローを起こさずに計算します。加算と2倍の操作を繰り返すことで、大きな数同士の積でも安全に求められます。

Begin
    ll mulmod(ll a, ll b, ll m)
    ll x = 0, y = a mod m
    while (b > 0)
        if (b mod 2 == 1)
            compute x = (x + y) mod m
        y = (y * 2) mod m
        b = b / 2
    return x mod m
End

2. modulo:繰り返し二乗法によるべき剰余

base^e mod m を効率よく計算します。指数 e を2で割りながら処理を進めることで、計算量を O(log e) に抑えます。

Begin
    ll modulo(ll base, ll e, ll m)
    Initialize:
    ll x = 1
    ll y = base
    while (e > 0)
        if (e mod 2 == 1)
            x = (x * y) mod m
        y = (y * y) mod m
        e = e / 2
    return x mod m
End

3. Miller:メインの素数判定

p − 1 を 2 で割れる限り割って s を求め、ランダムに選んだ底 a に対して判定条件を満たすかを確認します。指定した反復回数だけ試行し、すべて通過すれば「おそらく素数」と判断します。

Begin
    bool Miller(ll p, int iteration)
    if (p < 2)
        return false
    if (p != 2 and p mod 2 == 0)
        return false
    Compute: ll s = p - 1
    while (s mod 2 == 0)
        s = s / 2
    for i = 0 to iteration - 1
        Do
            ll a = rand() mod (p - 1) + 1, temp = s
            ll mod = modulo(a, temp, p)
            while (temp != p - 1 and mod != 1 and mod != p - 1)
                mod = mulmod(mod, mod, p)
                temp *= 2
            if (mod != p - 1 && temp % 2 == 0)
                return false
            else
                return true
End

サンプルコード

以下は、上記のアルゴリズムをC++で実装した完全なプログラムです。

#include <iostream>
#include<stdlib.h>
#define ll long long
using namespace std;
ll mulmod(ll a, ll b, ll m) {
   ll x = 0, y = a % m;
   while (b > 0) {
      if (b % 2 == 1) {
         x = (x + y) % m;
      }
      y = (y * 2) % m;
      b /= 2;
   }
   return x % m;
}

ll modulo(ll base, ll e, ll m) {
   ll x = 1;
   ll y = base;
   while (e > 0) {
      if (e % 2 == 1)
         x = (x * y) % m;
      y = (y * y) % m;
      e = e / 2;
   }
   return x % m;
}

bool Miller(ll p, int iteration) {
   if (p < 2) {
      return false;
   }
   if (p != 2 && p % 2 == 0) {
      return false;
   }
   ll s = p - 1;
   while (s % 2 == 0) {
      s /= 2;
   }
   for (int i = 0; i < iteration; i++) {
      ll a = rand() % (p - 1) + 1, temp = s;
      ll mod = modulo(a, temp, p);
      while (temp != p - 1 && mod != 1 && mod != p - 1) {
         mod = mulmod(mod, mod, p);
         temp *= 2;
      }
      if (mod != p - 1 && temp % 2 == 0) {
         return false;
      }
   }
   return true;
}

int main() {
   int iteration = 10;
   ll num;
   cout<<"Enter integer to test primality: ";
   cin>>num;
   if (Miller(num, iteration))
      cout<<num<<" is prime"<<endl;
   else
      cout<<num<<" is not prime"<<endl;
   return 0;
}

実行結果

Enter integer to test primality: 26
26 is not prime

この例では、26 を入力すると「26 is not prime(26 は素数ではない)」と正しく判定されています。

まとめ

ラビン・ミラー素数性テストは確率的な手法ですが、反復回数を増やせば誤判定の確率は指数的に減少し、実用上ほぼ信頼できる素数判定として扱えます。RSA鍵生成のように巨大な素数が必要となる場面で特に威力を発揮するため、ぜひ実装を理解しておきましょう。

  1. 指定した数値がハッピー数かどうかを判定するPythonプログラム

    特定の数値がハッピー数(Happy Number)であるかどうかを判定するには、「%」演算子(剰余)、「//」演算子(整数除算)、そして「+」演算子を組み合わせて使用します。 ハッピー数とは、各桁の数字を2乗して合計し、その結果に対して同じ操作を繰り返したとき、最終的に1に到達する数値のことです。1に到達しない数値は、4と5などの循環に入り込み、永遠に1になることはありません。 以下に具体的な実装例を示します。 サンプルコード def check_happy_num(my_num): remaining = sum_val = 0 while(my_num > 0):

  2. Pythonで指定した数がディサリウム数(Disarium Number)かどうかを判定する方法

    ある整数がディサリウム数(Disarium Number)であるかどうかを判定するには、各桁の数字を「その桁の位置」でべき乗し、それらを合計した値が元の数と一致するかを確認します。計算の前に、まず対象の数が何桁であるかを求めておく必要があります。 ディサリウム数とは? ディサリウム数とは、各桁の数字をそれぞれの位置(左から1桁目は1乗、2桁目は2乗…)でべき乗した合計が、元の数そのものと等しくなる数のことです。 例えば 135 は次のように計算できます。 135 = 1¹ + 3² + 5³ = 1 + 9 + 125 = 135 このように合計が元の数と一致するため、135 はディサリウム数