[PHP]多倍長整数の商と余りを同時に取得:gmp_div_qr 関数の完全解説

PHP

はじめに

PHP で非常に大きな数値を扱う場合、標準の整数型では桁あふれの問題が発生します。GMP(GNU Multiple Precision)拡張モジュールは、そんな状況で威力を発揮します。今回は GMP 拡張モジュールの中でも特に便利な gmp_div_qr 関数について詳しく解説します。この関数は、除算の「商」と「余り」を一度に取得できる効率的な関数です。

gmp_div_qr 関数とは?

gmp_div_qr 関数は、多倍長整数の除算を行い、商と余りの両方を一度に返す関数です。gmp_div_q(商のみ)や gmp_div_r(余りのみ)と異なり、1回の関数呼び出しで両方の値を取得できる点が大きな特徴です。

基本的な構文

gmp_div_qr(GMP $num1, GMP $num2, int $rounding_mode = GMP_ROUND_ZERO): array
  • $num1: 被除数(割られる数)
  • $num2: 除数(割る数)
  • $rounding_mode: 丸めモード(オプション、デフォルトは GMP_ROUND_ZERO)
  • 戻り値: 商と余りを含む配列 [商, 余り]

丸めモードについて

gmp_div_qr では、除算結果の丸め方を指定できます:

  • GMP_ROUND_ZERO: ゼロ方向への切り捨て(デフォルト)
  • GMP_ROUND_PLUSINF: 正の無限大方向への切り上げ
  • GMP_ROUND_MINUSINF: 負の無限大方向への切り下げ

使用例

基本的な使い方

<?php
// 25を7で割る
$a = gmp_init("25");
$b = gmp_init("7");
$result = gmp_div_qr($a, $b);

echo "商: " . gmp_strval($result[0]) . "\n"; // 出力: 3
echo "余り: " . gmp_strval($result[1]);     // 出力: 4
?>

丸めモードによる違いの例

<?php
// -25を7で割る(丸めモードによる違いを確認)
$a = gmp_init("-25");
$b = gmp_init("7");

// ゼロ方向への切り捨て
$result1 = gmp_div_qr($a, $b, GMP_ROUND_ZERO);
echo "GMP_ROUND_ZERO:\n";
echo "  商: " . gmp_strval($result1[0]) . "\n";  // -3
echo "  余り: " . gmp_strval($result1[1]) . "\n\n"; // -4

// 正の無限大方向への切り上げ
$result2 = gmp_div_qr($a, $b, GMP_ROUND_PLUSINF);
echo "GMP_ROUND_PLUSINF:\n";
echo "  商: " . gmp_strval($result2[0]) . "\n";  // -3
echo "  余り: " . gmp_strval($result2[1]) . "\n\n"; // -4

// 負の無限大方向への切り下げ
$result3 = gmp_div_qr($a, $b, GMP_ROUND_MINUSINF);
echo "GMP_ROUND_MINUSINF:\n";
echo "  商: " . gmp_strval($result3[0]) . "\n";  // -4
echo "  余り: " . gmp_strval($result3[1]) . "\n"; // 3
?>

実践的な活用例:大きな数値の除算

暗号計算や精密な数学計算で非常に大きな数値を扱う場合の例:

<?php
// 非常に大きな数値の除算
$large_number = gmp_init("98765432109876543210987654321");
$divisor = gmp_init("123456789");
$result = gmp_div_qr($large_number, $divisor);

echo "商: " . gmp_strval($result[0]) . "\n";
echo "余り: " . gmp_strval($result[1]);

// 検証:元の数値 = 商 × 除数 + 余り
$verification = gmp_add(gmp_mul($result[0], $divisor), $result[1]);
echo "\n\n検証結果: " . gmp_strval($verification);
// 元の数値と一致するはず
?>

パフォーマンスの利点

gmp_div_qr は、gmp_div_qgmp_div_r を別々に呼び出すよりも効率的です。内部的には1回の計算で商と余りの両方を求めるため、処理速度の向上が期待できます:

<?php
// 大きな数値を処理する場合
$a = gmp_init("987654321098765432109876543210");
$b = gmp_init("12345678");

// 方法1: gmp_div_qr を使用(効率的)
$start = microtime(true);
$result = gmp_div_qr($a, $b);
$q1 = $result[0];
$r1 = $result[1];
$time1 = microtime(true) - $start;

// 方法2: 個別に計算(非効率的)
$start = microtime(true);
$q2 = gmp_div_q($a, $b);
$r2 = gmp_div_r($a, $b);
$time2 = microtime(true) - $start;

echo "gmp_div_qr 処理時間: " . $time1 . " 秒\n";
echo "個別計算処理時間: " . $time2 . " 秒\n";
echo "速度向上率: " . (($time2 / $time1) - 1) * 100 . "%";
?>

注意点

  1. ゼロによる除算: 除数が 0 の場合、警告が発生し処理が中断されます。
  2. GMP オブジェクト: PHP 7.2 以降では、GMP リソースではなく GMP オブジェクトが使用されます。
  3. 戻り値の配列: 返される配列は常に2つの要素を持ち、インデックス0が商、インデックス1が余りです。
  4. 丸めモードの影響: 特に負の数を扱う場合、丸めモードによって結果が大きく変わることがあります。

関連する GMP 除算関数との違い

関数名戻り値特徴
gmp_div_q商のみ余りは捨てる
gmp_div_r余りのみ商は捨てる
gmp_div_qr商と余り両方を一度に計算(効率的)
gmp_div商のみgmp_div_q のエイリアス
gmp_mod余りのみ常に非負の余りを返す

まとめ

gmp_div_qr 関数は、PHP で大きな整数の除算を行う際に、商と余りを効率的に一度に取得できる強力なツールです。暗号計算、科学計算、精密な金融計算など、多倍長整数を扱う場面で特に役立ちます。

丸めモードを適切に選択することで、異なる除算の振る舞いを制御でき、様々な要件に対応できます。パフォーマンスを重視するアプリケーションでは、個別に商と余りを計算するよりも gmp_div_qr を使用することで処理効率が向上します。

大きな整数の演算が必要な際は、GMP 拡張モジュールとその関数群、特に効率的な gmp_div_qr 関数を積極的に活用しましょう。

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