はじめに
数学計算においてよく使用される「階乗」は、数が大きくなるとすぐに巨大な値になります。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...
?>
注意点
- 負の数:
gmp_fact
は負の数の階乗を計算できません。負の値を渡すとエラーが発生します。 - 0 の階乗: 数学的には 0! = 1 と定義されており、
gmp_fact(0)
も 1 を返します。 - メモリと処理時間: 非常に大きな階乗(例:1000!)は、膨大なメモリと処理時間を要する可能性があります。実用上は適切な上限を設けることをお勧めします。
- 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
関数の使用を検討してみることをお勧めします。