【PHP】整数論関連ツール PHP-Math-Integer

PHP

PHP-Math-Integer について

高校数学Aで取り扱う整数論関連ツールをPHPで作ってみました。

Githubで公開しています。Composerでインストールできます。

GitHub - macocci7/PHP-Math-Integer: Math library of number theory (only natural numbers)
Math library of number theory (only natural numbers) - GitHub - macocci7/PHP-Math-Integer: Math library of number theory...

素数判定、素数生成、素因数分解、最大公約数、最小公倍数、ユークリッドの互除法、不定方程式(ベズーの等式)、分数の約分などを取り扱うことができます。

ただし、対象は自然数のみです。

環境要件

  • PHP 8.0.0 (CLI) 以降
  • Composer

インストール

作業用ディレクトリを作り、そこでコマンドを実行します。

composer require macocci7/php-math-integer

クラス一覧

  • src/Number.php: 数に関する基本機能
  • src/Prime.php: 素数に関する基本機能
  • src/Divisor.php: 約数に関する基本機能
  • src/Multiple.php: 倍数に関する基本機能
  • src/Euclid.php: ユークリッドの互除法に関する基本機能
  • src/Bezout.php: ベズーの等式に関する基本機能
  • src/Fraction.php: 分数に関する基本機能(おまけ)

基本的な使い方

パッケージに同梱されている example をコピーしていじってみましょう。

cp -ra vendor/macocci7/php-math-integer/example/ ./

各使用例について説明していきます。

Number.php の使用例

example/UseNumber.php を見てみましょう。

<?php

/**
 * Examples of operation with
 * Macocci7\PhpMathInteger\Number
 */

 require_once('../vendor/autoload.php');

use Macocci7\PhpMathInteger\Number;

$n = new Number();

var_dump(
    $n->isInt(1), // true
    $n->isIntAll([ 1, 2, 3, ]), // true
    $n->isNatural(1), // true
    $n->isNaturalAll([ 1, 2, 3, ]), // true
    $n->isFloat(1.2), // true
    $n->isFloatAll([ -2.1, 0.0, 1.2, ]), // true
    $n->isNumber(1.2), // true
    $n->isNumberAll([ -2, 0.1, 3, ]), // true
    $n->isFraction(0.1), // true
    $n->isFractionAll([ -0.99, 0.1, 0.99]), // true
    $n->sign(-2.5), // -1
    $n->int(3.14), // 3
    $n->fraction(3.14), // 0.1400...
    $n->nthDigit(-3, 123.456), // 6
    $n->numberOfDigits(-123.4567), // 3
    $n->numberOfFractionalDigits(-12.3456), // 4
);

数に関する基本機能を揃えています。

実行してみましょう。

php -f UseNumber.php > UseNumber.txt

example/UseNumber.txt というテキストファイルに実行結果が保存されています。

開いて中を見てみましょう。

bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
int(-1)
int(3)
float(0.14000000000000012)
int(6)
int(3)
int(4)

大体はメソッド名で内容の判別ができると思います。

最後の3つのメソッドだけ説明をしておきます。

 $n->nthDigit(-3, 123.456), // 6

第2引数の数値の、第1引数桁目の数値を返します。

桁数の数え方は、第二引数の少数点を起点に左へ正、右へ負となります。

この例の場合は、少数点から右へ3桁目なので6となります。

$n->numberOfDigits(-123.4567), // 3

このメソッドは引数の数値の絶対値の整数部の桁数を返します。

この場合は、整数部は123なので桁数は3となります。

$n->numberOfFractionalDigits(-12.3456), // 4

このメソッドは引数の数値の小数部の桁数を返します。

この場合は、小数部は.3456なので桁数は4となります。

Prime.php の使用例

example/UsePrime.php を開いて見てみましょう。

素数関連の基本機能の使用例です。

<?php

/**
 * Examples of operation with
 * Macocci7\PhpMathInteger\Prime
 */

 require_once('../vendor/autoload.php');

use Macocci7\PhpMathInteger\Prime;

$p = new Prime();

// judge if $n is prime or not
$n = 3;
echo sprintf("Is %d prime? - %s.\n", $n, $p->isPrime($n) ? 'Yes' : 'No');

// judge if all of $n are prime or not
$n = [ 2, 3, 5, ];
echo sprintf(
    "Are all of [%s] prime? - %s.\n",
    implode(', ', $n),
    $p->isPrimeAll($n) ? 'Yes' : 'No'
);

// a prime next to $n
$n = 5;
echo sprintf("A prime next to %d is %d.\n", $n, $p->next($n));

// primes between $a and $b
$a = 6;
$b = 14;
echo sprintf(
    "Primes between %d and %d are [%s].\n",
    $a,
    $b,
    implode(', ', $p->between($a, $b))
);

// factorize
$n = 1234567890;
echo sprintf("Factorize %d:\n", $n);
$r = $p->factorize($n);
$l1 = $p->numberOfDigits(max(array_column($r, 0)));
$l2 = $p->numberOfDigits(max(array_column($r, 1)));
$s = str_repeat(' ', $l1 + 1);
$b = $s . str_repeat('-', $l2);
foreach ($r as $f) {
    echo (
        $f[0]
        ? sprintf("%" . $l1 . "d)%" . $l2 . "d\n%s\n", $f[0], $f[1], $b)
        : sprintf("%s%" . $l2 . "d\n", $s, $f[1])
    );
}

// Factorized formula
echo $n . " = " . $p->factorizedFormula($n)['formula'] . "\n";

実行してみましょう。

php -f UsePrime.php > UsePrime.txt

example/UsePrime.txt というファイルができています。

中を見てみましょう。

Is 3 prime? - Yes.
Are all of [2, 3, 5] prime? - Yes.
A prime next to 5 is 7.
Primes between 6 and 14 are [7, 11, 13].
Factorize 1234567890:
   2)1234567890
     ----------
   3) 617283945
     ----------
   3) 205761315
     ----------
   5)  68587105
     ----------
3607)  13717421
     ----------
           3803
1234567890 = 2 * 3 ^ 2 * 5 * 3607 * 3803

見れば大体内容がわかると思います。

掻い摘んで説明していきます。

$p->between($a, $b)

このメソッドは、第一引数と第二引数の間の素数を、小さい順に配列で返します。

$p->factorize($n)

このメソッドは、引数を素因数分解して、その結果を配列で返します。

例えば、30を素因数分解すると次のような配列が返ります。

array(3) {
  [0]=>
  array(2) {
    [0]=>
    int(2)
    [1]=>
    int(30)
  }
  [1]=>
  array(2) {
    [0]=>
    int(3)
    [1]=>
    int(15)
  }
  [2]=>
  array(2) {
    [0]=>
    NULL
    [1]=>
    int(5)
  }
}

この配列は、30を素因数分解した次の結果に対応しています。

2)30
  --
3)15
  --
   5

出力時に桁が揃うように調整しています。

$p->factorizedFormula($n)['formula']

このメソッドは、引数を素因数の積で表した式を返します。

戻り値が色々とてんこ盛りの配列なので、その中から[‘formula’]のみを取り出しています。

Divisor.php の使用例

example/UseDivisor.php を見てみましょう。

約数関連の基本機能の使用例です。

<?php

/**
 * Examples of operation with
 * Macocci7\PhpMathInteger\Euclid
 */

 require_once('../vendor/autoload.php');

use Macocci7\PhpMathInteger\Divisor;

$d = new Divisor();
$a = 12;
$b = 18;

// Number of divisors
echo sprintf("%d has %d divisors.\n", $a, $d->count($a));

// List of all divisors
echo sprintf("[%s]\n", implode(', ', $d->list($a)));

// Common factors
echo sprintf("%d = %s\n", $a, $d->formula($a));
echo sprintf("%d = %s\n", $b, $d->formula($b));
echo sprintf(
    "common factors : %s\n",
    $d->formula($d->value($d->commonFactors($a, $b)))
);
echo sprintf(
    "common divisors : [%s]\n",
    implode(', ', $d->commonDivisors($a, $b))
);

// greatest common factor (divisor)
echo sprintf(
    "greatest common factor (divisor) : %s\n",
    $d->greatestCommonFactor($a, $b)
);

// Reducing fraction
$r = $d->reduceFraction($a, $b);
$ra = $d->value($r[0]);
$rb = $d->value($r[1]);
echo sprintf("%d/%d reduces to %d/%d\n", $a, $b, $ra, $rb);

実行してみましょう。

php -f UseDivisor.php > UseDivisor.txt

example/UseDivisor.txt というファイルができているので、中を見てみましょう。

12 has 6 divisors.
[1, 2, 3, 4, 6, 12]
12 = 2 ^ 2 * 3
18 = 2 * 3 ^ 2
common factors : 2 * 3
common divisors : [1, 2, 3, 6]
greatest common factor (divisor) : 6
12/18 reduces to 2/3

これも見れば大体内容がわかると思います。

面倒な箇所だけ掻い摘んで説明します。

$d->formula($d->value($d->commonFactors($a, $b)))

$d->commonFactors($a, $b) は、引数同士の共通因数を特殊な配列で返します。

例えば、12と18の共通因数は 2 * 3 ですが、次のような配列が返ります。

array(2){
  [2] =>
  int(1)
  [3] =>
  int(1)
}

ハッシュキーが基数、ハッシュの値が指数となっています。

$d->value($d->commonFactors($a, $b)) は、この配列を基に数値を返します。

$d->formula($d->value($d->commonFactors($a, $b))) は、数値を基に素因数の積で式を返します。

Multiple.php の使用例

example/UseMultiple.php を開いて見てみましょう。

倍数関連の基本機能の使用例です。

<?php

require_once('../vendor/autoload.php');

use Macocci7\PhpMathInteger\Multiple;

$m = new Multiple();
$a = 12;
$b = 18;

// least common multiple
echo sprintf(
    "least common multiple of %d and %d is %d\n",
    $a,
    $b,
    $m->leastCommonMultiple($a, $b)
);

実行してみましょう。

php -f UseMultiple.php > UseMultiple.txt

example/UseMultiple.txt というファイルが出来ているので、開いて見てみましょう。

least common multiple of 12 and 18 is 36

特に説明は不要だと思います。

Euclid.php の使用例

example/UseEuclid.php を開いて見てみましょう。

ユークリッド互除法関連の基本機能の使用例です。

<?php

/**
 * Examples of operation with
 * Macocci7\PhpMathInteger\Euclid
 */

 require_once('../vendor/autoload.php');

use Macocci7\PhpMathInteger\Euclid;

// Presets values
$e = new Euclid();
$a = 390;
$b = 273;
$c = 39;

// Judges if $c is GCD($a, $b) or not
echo sprintf(
    "Is %d GCD(%d, %d)? - %s.\n",
    $c,
    $a,
    $b,
    $e->isGcdOf($c, $a, $b) ? 'Yes' : 'No'
);

// Euclidean Algorithm
$r = $e->run($a, $b);
echo "Euclidean Algorithm:\n";
foreach ($r['processText'] as $t) {
    echo $t . "\n";
}

// Formula of remainders
echo "Remainders can be expressed as:\n";
foreach ($r['processData'] as $d) {
    echo sprintf("%d = %d - %d * %d\n", $d['r'], $d['a'], $d['b'], $d['c']);
}

// Judges if $a and $b are coprime or not
echo sprintf(
    "Are %d and %d coprime? - %s.\n",
    $a,
    $b,
    $e->isCoprime($a, $b) ? 'Yes' : 'No'
);

// GCD($a, $b)
echo sprintf(
    "Because the Greatest Common Divisor of %d and %d is %d.\n",
    $a,
    $b,
    $e->gcd($a, $b)
);

実行してみましょう。

php -f UseEuclid.php > UseEuclid.txt

example/UseEuclid.txt というファイルが出来ているので、開いて見てみましょう。

Is 39 GCD(390, 273)? - Yes.
Euclidean Algorithm:
390 = 273 * 1 + 117
273 = 117 * 2 + 39
117 = 39 * 3 + 0
Remainders can be expressed as:
117 = 390 - 273 * 1
39 = 273 - 117 * 2
0 = 117 - 39 * 3
Are 390 and 273 coprime? - No.
Because the Greatest Common Divisor of 390 and 273 is 39.

これも見れば内容が大体わかると思いますが、

用語的に掻い摘んで説明しておくと、

coprime とは教科書上では「互いに素」という言葉です。

「a と b が互いに素」とは、「a と b の最大公約数が1のみ」

ということです。ただし、a ≠ b です。

互いに素の組み合わせ例:

(a, b) = (1, 2), (2, 3), (3, 4), (4, 5), …, (8, 9), …

Bezout.php の使用例

example/UseBezout.php を開いて見てみましょう。

(2元1次)不定方程式(ベズーの等式)関連の基本機能の使用例です。

<?php

/**
 * Examples of operation with
 * Macocci7\PhpMathInteger\Bezout
 */

 require_once('../vendor/autoload.php');

use Macocci7\PhpMathInteger\Bezout;

// Bezout's Equation: 3x + 4y = 1
$b = new Bezout([3, 4, 1, ]);
echo sprintf("Bezout's Equation: %s\n", $b->equation());

// Solvable or not
echo sprintf("Is it solvable? - %s.\n", ($b->isSolvable() ? 'Yes' : 'No'));

// A solution set
$s = $b->solution()['solution'];
echo sprintf("A solutionset: (x, y) = (%d, %d)\n", $s['x'], $s['y']);

// General solution
$g = $b->generalSolution()['generalSolution']['formula'];
echo sprintf("General solution:\n\t%s\n\t%s\n", $g['x'], $g['y']);

実行してみましょう。

php -f UseBezout.php > UseBezout.txt

example/UseBezout.txt というファイルが出来ているので、開いて見てみましょう。

Bezout's Equation: 3x + 4y = 1
Is it solvable? - Yes.
A solutionset: (x, y) = (-1, 1)
General solution:
	x = 4k - 1
	y = 3k + 1

これも見れば大体内容がわかると思います。

※本来、ベズーの等式の英訳は Bezout’s Identity です。

Fraction.php の使用例

example/UseFraction.php を開いて見てみましょう。

分数関連の基本機能の使用例です。(おまけ機能です)

<?php

require_once('../vendor/autoload.php');

use Macocci7\PhpMathInteger\Fraction;

// prest: 1 and 2/4
$f = new Fraction('1 2/4');

// is reduced or not?
echo $f->text() . " is " . ($f->isReduced() ? '' : 'not ') . "reduced.\n";

// is proper or not?
echo $f->text() . " is " . ($f->isProper() ? '' : 'not ') . "a proper fraction.\n";

// is improper or not?
echo $f->text() . " is " . ($f->isImproper() ? '' : 'not ') . "a improper fraction.\n";

// is mixed or not?
echo $f->text() . " is " . ($f->isMixed() ? '' : 'not ') . "a mixed fraction.\n";

// convert $f into a improper fraction
echo $f->text() . " = " . $f->improper()->text() . "\n";

// convert $f into a mixed fraction
echo $f->text() . " = " . $f->mixed()->text() . "\n";

// reduce fraction
echo $f->text() . " reduces to " . $f->reduce()->text() . "\n";

// integral part
echo "integral part of " . $f->text() . " is " . $f->int() . "\n";

// change into a float
echo $f->text() . " = " . $f->float() . "\n";

// four arithmetic operations
$f1 = new Fraction('1/3');
$f2 = new Fraction('1/6');
echo $f1->text() . ' + ' . $f2->text() . ' = ' . $f1->add($f2)->text() . "\n";
$f1->set('2/3');
$f2->set('1/6');
echo $f1->text() . ' - ' . $f2->text() . ' = ' . $f1->substruct($f2)->text() . "\n";
$f1->set('2/3');
$f2->set('1/6');
echo $f1->text() . ' * ' . $f2->text() . ' = ' . $f1->multiply($f2)->text() . "\n";
$f1->set('2/3');
$f2->set('1/6');
echo $f1->text() . ' / ' . $f2->text() . ' = ' . $f1->divide($f2)->text() . "\n";

// reduce fractions to a common denominator
$f1->set('1/3');
$f2->set('2/5');
echo "reduce the fractions of " . $f1->text() . " and " . $f2->text()
    . " to a common denominator:\n";
$f1->toCommonDenominator($f2);
echo $f1->text() . " and " . $f2->text() . "\n";

実行してみましょう。

php -f UseFraction.php > UseFraction.txt

example/UseFraction.txt というファイルが出来ているので、開いて見てみましょう。

1 2/4 is not reduced.
1 2/4 is not a proper fraction.
1 2/4 is not a improper fraction.
1 2/4 is a mixed fraction.
1 2/4 = 6/4
6/4 = 1 2/4
1 2/4 reduces to 1 1/2
integral part of 1 1/2 is 1
1 1/2 = 1.5
1/3 + 1/6 = 1/2
2/3 - 1/6 = 1/2
2/3 * 1/6 = 1/9
2/3 / 1/6 = 4/1
reduce the fractions of 1/3 and 2/5 to a common denominator:
5/15 and 6/15

これも見れば大体内容がわかると思いますが、

用語的に掻い摘んで説明していきます。

「1 2/4」の表記は日本語では見かけませんね。

これは帯分数の「1と4分の2」のことです。

「is reduced」は「既約分数である」ということです。

「proper fraction」は分子が分母より小さい分数。

「improper fraction」は分子が分母より大きい仮分数。

「mixed fraction」は帯分数。

「reduce the fractions to a common denominator」は「通分する」ということです。

今後の課題

  • Number.php: まだ必要な機能がある。
  • Multiple.php: まだ必要な機能がある。
  • Fraction.php: 整数、少数との演算は?
  • n進数の底変換が未実装
  • 一般整数への対応は?

コメント

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