public static interpolate ( $source, $args ) : |
||
$source | The source of our approximation. Should be either a callback function or a set of arrays. Each array (point) contains precisely two numbers, an x and y. Example array: [[1,2], [2,3], [3,4]]. Example callback: function($x) {return $x**2;} | |
return | The interpolting (piecewise) polynomial, as an instance of Piecewise. |
public static function interpolate($source, ...$args)
{
// Get an array of points from our $source argument
$points = self::getPoints($source, $args);
// Validate input and sort points
self::validate($points, $degree = 1);
$sorted = self::sort($points);
// Descriptive constants
$x = self::X;
$y = self::Y;
// Initialize
$n = count($sorted);
$k = $n - 1;
$h = [];
$a = [];
$μ = [0];
$z = [0];
$z[$k] = 0;
$c = [];
$c[$k] = 0;
$poly = [];
for ($i = 0; $i < $k; $i++) {
$xᵢ = $sorted[$i][$x];
$xᵢ₊₁ = $sorted[$i + 1][$x];
$a[$i] = $sorted[$i][$y];
$h[$i] = $xᵢ₊₁ - $xᵢ;
if ($i == 0) {
continue;
}
$xᵢ₋₁ = $sorted[$i - 1][$x];
$f⟮xᵢ⟯ = $sorted[$i][$y];
// yᵢ
$f⟮xᵢ₊₁⟯ = $sorted[$i + 1][$y];
// yᵢ₊₁
$f⟮xᵢ₋₁⟯ = $sorted[$i - 1][$y];
// yᵢ₋₁
$α = 3 / $h[$i] * ($f⟮xᵢ₊₁⟯ - $f⟮xᵢ⟯) - 3 / $h[$i - 1] * ($f⟮xᵢ⟯ - $f⟮xᵢ₋₁⟯);
$l = 2 * ($xᵢ₊₁ - $xᵢ₋₁) - $h[$i - 1] * $μ[$i - 1];
$μ[$i] = $h[$i] / $l;
$z[$i] = ($α - $h[$i - 1] * $z[$i - 1]) / $l;
}
for ($i = $k - 1; $i >= 0; $i--) {
$xᵢ = $sorted[$i][$x];
$xᵢ₊₁ = $sorted[$i + 1][$x];
$f⟮xᵢ⟯ = $sorted[$i][$y];
// yᵢ
$f⟮xᵢ₊₁⟯ = $sorted[$i + 1][$y];
// yᵢ₊₁
$c[$i] = $z[$i] - $μ[$i] * $c[$i + 1];
$b[$i] = ($f⟮xᵢ₊₁⟯ - $f⟮xᵢ⟯) / $h[$i] - $h[$i] * ($c[$i + 1] + 2 * $c[$i]) / 3;
$d[$i] = ($c[$i + 1] - $c[$i]) / (3 * $h[$i]);
$poly[$i] = new Polynomial([$d[$i], $c[$i] - 3 * $d[$i] * $xᵢ, $b[$i] - 2 * $c[$i] * $xᵢ + 3 * $d[$i] * $xᵢ ** 2, $a[$i] - $b[$i] * $xᵢ + $c[$i] * $xᵢ ** 2 - $d[$i] * $xᵢ ** 3]);
if ($i == 0) {
$int[$i] = [$xᵢ, $xᵢ₊₁];
} else {
$int[$i] = [$xᵢ, $xᵢ₊₁, true, false];
}
}
$piecewise = new Piecewise($int, $poly);
return $piecewise;
}
public function testSolveNonzeroError() { // f(x) = x⁴ + 8x³ -13x² -92x + 96 $f = new Polynomial([1, 8, -13, -92, 96]); // The error is bounded by: // |f(x)-p(x)| = tol <= (1/4!) * h⁴ * max f⁽⁴⁾(x) // where h = max hᵢ // f'(x) = 4x³ +24x² -26x - 92 // f''(x) = 12x² - 48x - 26 // f'''(x) = 24x - 48 // f⁽⁴⁾(x) = 24 $a = 0; $b = 10; $n = 51; // So, tol <= (1/24) * (1/5)⁴ * 24 = (1/5)⁴ $tol = 0.2 ** 4; $roundoff = 1.0E-6; // round off error $p = NaturalCubicSpline::interpolate($f, $a, $b, $n); // Check that p(x) agrees with f(x) at x = 0 $target = 0; $expected = $f($target); $x = $p($target); $this->assertEquals($expected, $x, '', $tol + $roundoff); // Check that p(x) agrees with f(x) at x = 2 $target = 2; $expected = $f($target); $x = $p($target); $this->assertEquals($expected, $x, '', $tol + $roundoff); // Check that p(x) agrees with f(x) at x = 4 $target = 4; $expected = $f($target); $x = $p($target); $this->assertEquals($expected, $x, '', $tol + $roundoff); // Check that p(x) agrees with f(x) at x = 6 $target = 6; $expected = $f($target); $x = $p($target); $this->assertEquals($expected, $x, '', $tol + $roundoff); // Check that p(x) agrees with f(x) at x = 8 $target = 8; $expected = $f($target); $x = $p($target); $this->assertEquals($expected, $x, '', $tol + $roundoff); // Check that p(x) agrees with f(x) at x = 10 $target = 10; $expected = $f($target); $x = $p($target); $this->assertEquals($expected, $x, '', $tol + $roundoff); // Check that p(x) agrees with f(x) at x = 7.32 (not a node) $target = 7.32; $expected = $f($target); $x = $p($target); $this->assertEquals($expected, $x, '', $tol + $roundoff); }