[PHP]大きな階乗を計算する:gmp_fact 関数の完全解説

PHP

はじめに

数学計算においてよく使用される「階乗」は、数が大きくなるとすぐに巨大な値になります。PHP の標準整数型では扱いきれなくなることも多いため、GMP(GNU Multiple Precision)拡張モジュールの gmp_fact 関数が非常に役立ちます。今回は、この関数の使い方と活用例について詳しく解説します。

gmp_fact 関数とは?

gmp_fact 関数は、与えられた整数の階乗(n!)を計算する GMP ライブラリの関数です。非常に大きな階乗値も正確に計算できるのが特徴です。

基本的な構文

gmp_fact(int|GMP $num): GMP
  • $num: 階乗を計算したい整数(整数値または GMP オブジェクト)
  • 戻り値: 階乗の結果を表す GMP オブジェクト

階乗とは?

階乗(Factorial)は、数学記号「!」で表され、ある正の整数 n に対して、1 から n までのすべての整数の積を意味します。

n! = n × (n-1) × (n-2) × … × 3 × 2 × 1

例えば、5! = 5 × 4 × 3 × 2 × 1 = 120 です。

使用例

基本的な使い方

<?php
// 5の階乗を計算
$factorial5 = gmp_fact(5);
echo "5! = " . gmp_strval($factorial5) . "\n"; // 出力: 5! = 120

// 10の階乗を計算
$factorial10 = gmp_fact(10);
echo "10! = " . gmp_strval($factorial10) . "\n"; // 出力: 10! = 3628800

// GMP オブジェクトを使用する場合
$num = gmp_init(15);
$factorial15 = gmp_fact($num);
echo "15! = " . gmp_strval($factorial15) . "\n"; // 出力: 15! = 1307674368000
?>

大きな階乗の計算

PHP の標準整数型では扱えない大きな階乗も計算できます:

<?php
// 30の階乗を計算
$factorial30 = gmp_fact(30);
echo "30! = " . gmp_strval($factorial30) . "\n";
// 出力: 30! = 265252859812191058636308480000000

// 50の階乗を計算
$factorial50 = gmp_fact(50);
echo "50! = " . gmp_strval($factorial50) . "\n";
// 50の階乗は非常に大きな数値になります
// 出力: 50! = 30414093201713378043612608166064768844377641568960512000000000000

// 100の階乗の桁数を調べる
$factorial100 = gmp_fact(100);
$digits = strlen(gmp_strval($factorial100));
echo "100! の桁数: " . $digits . " 桁\n";
// 出力例: 100! の桁数: 158 桁
?>

実践的な活用例

組み合わせ(nCr)の計算

組み合わせの計算には階乗が使われます。nCr = n! / (r! × (n-r)!) で計算できます:

<?php
/**
 * 組み合わせ nCr を計算する関数
 * @param int $n 全体の数
 * @param int $r 選ぶ数
 * @return GMP 組み合わせの結果
 */
function gmp_combination($n, $r) {
    // n! / (r! * (n-r)!)
    $n_fact = gmp_fact($n);
    $r_fact = gmp_fact($r);
    $n_minus_r_fact = gmp_fact($n - $r);
    
    $denominator = gmp_mul($r_fact, $n_minus_r_fact);
    return gmp_div_q($n_fact, $denominator);
}

// 計算例: 20C10(20個から10個を選ぶ組み合わせの数)
$combination = gmp_combination(20, 10);
echo "20C10 = " . gmp_strval($combination) . "\n";
// 出力: 20C10 = 184756

// より大きな値: 40C20
$large_combination = gmp_combination(40, 20);
echo "40C20 = " . gmp_strval($large_combination) . "\n";
// 出力: 40C20 = 137846528820
?>

数論での活用:ウィルソンの定理

ウィルソンの定理は、p が素数であることと (p-1)! ≡ -1 (mod p) が同値であるという定理です:

<?php
/**
 * ウィルソンの定理を使用して数が素数かどうかを確認する
 * @param int $p 確認したい数
 * @return bool 素数であれば true
 */
function is_prime_wilson($p) {
    if ($p <= 1) return false;
    if ($p <= 3) return true;
    if ($p % 2 == 0) return false;
    
    // (p-1)! mod p を計算
    $factorial = gmp_fact($p - 1);
    $remainder = gmp_mod($factorial, $p);
    
    // ウィルソンの定理: (p-1)! ≡ -1 (mod p) ⟺ p は素数
    // -1 mod p は p-1 に等しい
    return gmp_cmp($remainder, gmp_sub($p, 1)) === 0;
}

// テスト
$primes = [2, 3, 5, 7, 11, 13, 17, 19];
$non_primes = [4, 6, 8, 9, 10, 12, 15];

echo "素数のテスト:\n";
foreach ($primes as $num) {
    echo "$num: " . (is_prime_wilson($num) ? "素数" : "合成数") . "\n";
}

echo "\n合成数のテスト:\n";
foreach ($non_primes as $num) {
    echo "$num: " . (is_prime_wilson($num) ? "素数" : "合成数") . "\n";
}

// 注意: この方法は小さな数には適していますが、大きな数の素数判定には非効率です
?>

二項係数を使った確率計算

確率論でよく使われる二項分布の計算:

<?php
/**
 * 二項分布の確率質量関数を計算
 * @param int $n 試行回数
 * @param int $k 成功回数
 * @param float $p 成功確率
 * @return float 確率
 */
function binomial_probability($n, $k, $p) {
    // nCk を計算
    $combinations = gmp_combination($n, $k);
    
    // p^k * (1-p)^(n-k)
    $probability = pow($p, $k) * pow(1 - $p, $n - $k);
    
    // nCk * p^k * (1-p)^(n-k)
    return gmp_strval($combinations) * $probability;
}

// 例: 10回の試行で、成功確率0.3のとき、ちょうど3回成功する確率
$prob = binomial_probability(10, 3, 0.3);
echo "10回試行で3回成功する確率: " . $prob . "\n";
// 出力例: 10回試行で3回成功する確率: 0.2668279...
?>

注意点

  1. 負の数: gmp_fact は負の数の階乗を計算できません。負の値を渡すとエラーが発生します。
  2. 0 の階乗: 数学的には 0! = 1 と定義されており、gmp_fact(0) も 1 を返します。
  3. メモリと処理時間: 非常に大きな階乗(例:1000!)は、膨大なメモリと処理時間を要する可能性があります。実用上は適切な上限を設けることをお勧めします。
  4. GMP 拡張モジュール: この関数を使用するには、PHP に GMP 拡張モジュールがインストールされている必要があります。

パフォーマンス比較

gmp_fact 関数を使った階乗計算と、標準的なループによる階乗計算の比較:

<?php
// GMP を使用した階乗計算
function factorial_gmp($n) {
    return gmp_fact($n);
}

// 標準的なループによる階乗計算(オーバーフローの可能性あり)
function factorial_loop($n) {
    $result = 1;
    for ($i = 2; $i <= $n; $i++) {
        $result *= $i;
    }
    return $result;
}

// パフォーマンステスト
$n = 20; // テストする階乗

// GMP バージョン
$start = microtime(true);
$result_gmp = factorial_gmp($n);
$time_gmp = microtime(true) - $start;
echo "GMP による {$n}! の計算時間: " . $time_gmp . " 秒\n";
echo "結果: " . gmp_strval($result_gmp) . "\n\n";

// ループバージョン(PHP の整数型の範囲内で収まる値のみテスト可能)
$start = microtime(true);
$result_loop = factorial_loop($n);
$time_loop = microtime(true) - $start;
echo "ループによる {$n}! の計算時間: " . $time_loop . " 秒\n";
echo "結果: " . $result_loop . "\n";

// 注意: n が大きい場合、factorial_loop 関数はオーバーフローします
?>

まとめ

gmp_fact 関数は、PHP で大きな階乗を正確に計算するための強力なツールです。標準の整数型では扱えないような大きな数値も、この関数を使えば簡単に計算できます。

組み合わせ計算、確率論、数論など、階乗を必要とする数学的な計算において、gmp_fact は精度と効率の両面で非常に有用です。特に科学計算、統計処理、暗号関連のアプリケーションでの活用が期待できます。

PHP で大規模な数値計算が必要な場合は、GMP 拡張モジュールとその関数群、特に gmp_fact 関数の使用を検討してみることをお勧めします。

タイトルとURLをコピーしました