mirror of
https://github.com/Karaka-Management/phpOMS.git
synced 2026-01-10 17:28:40 +00:00
305 lines
6.5 KiB
PHP
Executable File
305 lines
6.5 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* Karaka
|
|
*
|
|
* PHP Version 8.1
|
|
*
|
|
* @package phpOMS\Math\Functions
|
|
* @copyright Dennis Eichhorn
|
|
* @license OMS License 1.0
|
|
* @version 1.0.0
|
|
* @link https://karaka.app
|
|
*/
|
|
declare(strict_types=1);
|
|
|
|
namespace phpOMS\Math\Functions;
|
|
|
|
/**
|
|
* Gamma function
|
|
*
|
|
* @package phpOMS\Math\Functions
|
|
* @license OMS License 1.0
|
|
* @link https://karaka.app
|
|
* @since 1.0.0
|
|
*/
|
|
final class Gamma
|
|
{
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @since 1.0.0
|
|
* @codeCoverageIgnore
|
|
*/
|
|
private function __construct()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Gamma function
|
|
*
|
|
* @param int|float $z Value
|
|
*
|
|
* @return float
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public static function gamma(int | float $z) : float
|
|
{
|
|
return \exp(self::logGamma($z));
|
|
}
|
|
|
|
/**
|
|
* approximation values.
|
|
*
|
|
* @var float[]
|
|
* @since 1.0.0
|
|
*/
|
|
private const LANCZOSAPPROXIMATION = [
|
|
0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059,
|
|
12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7,
|
|
];
|
|
|
|
/**
|
|
* Calculate gamma with Lanczos approximation
|
|
*
|
|
* @param int|float $z Value
|
|
*
|
|
* @return float
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public static function lanczosApproximationReal(int | float $z) : float
|
|
{
|
|
if ($z < 0.5) {
|
|
return \M_PI / (\sin(\M_PI * $z) * self::lanczosApproximationReal(1 - $z));
|
|
}
|
|
|
|
--$z;
|
|
$a = self::LANCZOSAPPROXIMATION[0];
|
|
$t = $z + 7.5;
|
|
|
|
for ($i = 1; $i < 9; ++$i) {
|
|
$a += self::LANCZOSAPPROXIMATION[$i] / ($z + $i);
|
|
}
|
|
|
|
return \sqrt(2 * \M_PI) * \pow($t, $z + 0.5) * \exp(-$t) * $a;
|
|
}
|
|
|
|
/**
|
|
* Calculate gamma with Stirling approximation
|
|
*
|
|
* @param int|float $x Value
|
|
*
|
|
* @return float
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public static function stirlingApproximation(int | float $x) : float
|
|
{
|
|
return \sqrt(2.0 * \M_PI / $x) * \pow($x / \M_E, $x);
|
|
}
|
|
|
|
/**
|
|
* Calculate gamma with Spouge approximation
|
|
*
|
|
* @param int|float $z Value
|
|
*
|
|
* @return float
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public static function spougeApproximation(int | float $z) : float
|
|
{
|
|
$k1_fact = 1.0;
|
|
$c = [\sqrt(2.0 * \M_PI)];
|
|
|
|
for ($k = 1; $k < 12; ++$k) {
|
|
$c[$k] = \exp(12 - $k) * \pow(12 - $k, $k - 0.5) / $k1_fact;
|
|
$k1_fact *= -$k;
|
|
}
|
|
|
|
$accm = $c[0];
|
|
for ($k = 1; $k < 12; ++$k) {
|
|
$accm += $c[$k] / ($z + $k);
|
|
}
|
|
|
|
$accm *= \exp(-$z - 12) * \pow($z + 12, $z + 0.5);
|
|
|
|
return $accm / $z;
|
|
}
|
|
|
|
/**
|
|
* Log of the gamma function
|
|
*
|
|
* @param int|float $z Value
|
|
*
|
|
* @return float
|
|
*
|
|
* @see Book: Numerical Recipes - 9780521406895
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public static function logGamma(int | float $z) : float
|
|
{
|
|
static $approx = [
|
|
76.18009172947146,-86.50532032941677,
|
|
24.01409824083091,-1.231739572450155,
|
|
0.1208650973866179e-2,-0.5395239384953e-5,
|
|
];
|
|
|
|
$y = $z;
|
|
|
|
$temp = $z + 5.5 - ($z + 0.5) * \log($z + 5.5);
|
|
$sum = 1.000000000190015;
|
|
|
|
for ($i = 0; $i < 6; ++$i) {
|
|
$sum += $approx[$i] / ++$y;
|
|
}
|
|
|
|
return -$temp + \log(\sqrt(2 * \M_PI) * $sum / $z);
|
|
}
|
|
|
|
/**
|
|
* Calculate gamma function value.
|
|
*
|
|
* Example: (7)
|
|
*
|
|
* @param int $k Variable
|
|
*
|
|
* @return int
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public static function getGammaInteger(int $k) : int
|
|
{
|
|
return Functions::fact($k - 1);
|
|
}
|
|
|
|
/**
|
|
* First or lower incomplete gamma function
|
|
*
|
|
* @param float $a a
|
|
* @param float $x Value
|
|
*
|
|
* @return float
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public static function incompleteGammaFirst(float $a, float $x) : float
|
|
{
|
|
return self::regularizedGamma($a, $x) * \exp(self::logGamma($a));
|
|
}
|
|
|
|
/**
|
|
* Second or upper incomplete gamma function
|
|
*
|
|
* @param float $a a
|
|
* @param float $x Value
|
|
*
|
|
* @return float
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public static function incompleteGammaSecond(float $a, float $x) : float
|
|
{
|
|
return \exp(self::logGamma($a)) - self::regularizedGamma($a, $x) * \exp(self::logGamma($a));
|
|
}
|
|
|
|
/**
|
|
* Incomplete gamma function
|
|
*
|
|
* @param float $a a
|
|
* @param float $x Value
|
|
*
|
|
* @return float
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public static function regularizedGamma(float $a, float $x) : float
|
|
{
|
|
if ($x <= 0.0 || $a <= 0.0 || $a > 10000000000.0) {
|
|
return 0.0;
|
|
} elseif ($x < $a + 1.0) {
|
|
return self::gammaSeriesExpansion($a, $x);
|
|
}
|
|
|
|
return 1.0 - self::gammaFraction($a, $x);
|
|
}
|
|
|
|
/**
|
|
* Gamma series expansion
|
|
*
|
|
* @param float $a a
|
|
* @param float $x Value
|
|
*
|
|
* @return float
|
|
*
|
|
* @see JSci
|
|
* @author Jaco van Kooten
|
|
* @license LGPL 2.1
|
|
* @since 1.0.0
|
|
*/
|
|
private static function gammaSeriesExpansion(float $a, float $x) : float
|
|
{
|
|
$ap = $a;
|
|
$del = 1.0 / $a;
|
|
$sum = $del;
|
|
|
|
for ($i = 1; $i < 150; ++$i) {
|
|
++$ap;
|
|
|
|
$del *= $x / $ap;
|
|
$sum += $del;
|
|
|
|
if ($del < $sum * 2.22e-16) {
|
|
return $sum * \exp(-$x + $a * \log($x) - self::logGamma($a));
|
|
}
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
/**
|
|
* Gamma fraction
|
|
*
|
|
* @param float $a a
|
|
* @param float $x Value
|
|
*
|
|
* @return float
|
|
*
|
|
* @see JSci
|
|
* @author Jaco van Kooten
|
|
* @license LGPL 2.1
|
|
* @since 1.0.0
|
|
*/
|
|
private static function gammaFraction(float $a, float $x) : float
|
|
{
|
|
$b = $x + 1.0 - $a;
|
|
$c = 1.0 / 1.18e-37;
|
|
$d = 1.0 / $b;
|
|
$h = $d;
|
|
$del = 0.0;
|
|
|
|
for ($i = 1; $i < 150 && \abs($del - 1.0) > 2.22e-16; ++$i) {
|
|
$an = - $i * ($i - $a);
|
|
$b += 2.0;
|
|
$d = $an * $d + $b;
|
|
$c = $b + $an / $c;
|
|
|
|
if (\abs($c) < 1.18e-37) {
|
|
$c = 1.18e-37;
|
|
}
|
|
|
|
if (\abs($d) < 1.18e-37) {
|
|
$d = 1.18e-37;
|
|
}
|
|
|
|
$d = 1.0 / $d;
|
|
$del = $d * $c;
|
|
$h *= $del;
|
|
}
|
|
|
|
return \exp(-$x + $a * \log($x) - self::logGamma($a)) * $h;
|
|
}
|
|
}
|