Skip to content

Instantly share code, notes, and snippets.

@mermshaus
Last active August 29, 2015 14:07
Show Gist options
  • Save mermshaus/e09579f882c1736c2e09 to your computer and use it in GitHub Desktop.
Save mermshaus/e09579f882c1736c2e09 to your computer and use it in GitHub Desktop.
findIntersection - Tries to find the first intersection of functions $f and $g within a given interval
<?php
/**
* Tries to find the first intersection of functions $f and $g within a given
* interval
*
* $discardExceptions allows $f and $g to reject/skip certain values (e. g.
* because of division by zero) by throwing an exception without having to stop
* the whole calculation.
*
* In theory, all intersections in the interval could be returned, not just the
* first one. But I couldn't be bothered to figure out whether the next
* intersection really is a *new* intersection and what to do when $f and $g
* have the same definition.
*
* @param Closure $f
* @param Closure $g
* @param int|float $from
* @param int|float $to
* @param int|float $step
* @param int|float $epsilon
* @param bool $discardExceptions
* @return array
* @throws InvalidArgumentException
* @throws Exception
*/
function findIntersection(Closure $f, Closure $g, $from, $to, $step, $epsilon = 0.00001, $discardExceptions = true)
{
$info = new ReflectionFunction($f);
if ($info->getNumberOfParameters() !== 1) {
throw new InvalidArgumentException('$f must accept exactly one parameter');
}
$info = new ReflectionFunction($g);
if ($info->getNumberOfParameters() !== 1) {
throw new InvalidArgumentException('$g must accept exactly one parameter');
}
if (!is_int($from) && !is_float($from)) {
throw new InvalidArgumentException('$from must be int|float');
}
if (!is_int($to) && !is_float($to)) {
throw new InvalidArgumentException('$to must be int|float');
}
if (!is_int($step) && !is_float($step)) {
throw new InvalidArgumentException('$step must be int|float');
}
if ((float) $step === 0.0) {
throw new InvalidArgumentException('$step cannot be 0');
}
if (
$from - $to > 0 && $step > 0
|| $from - $to < 0 && $step < 0
) {
throw new InvalidArgumentException('Stepping in the wrong direction');
}
if (!is_int($epsilon) && !is_float($epsilon)) {
throw new InvalidArgumentException('$epsilon must be int|float');
}
if (!is_bool($discardExceptions)) {
throw new InvalidArgumentException('$discardExceptions must be bool');
}
$ignoreActiveIteration = false;
$tmpf = 0;
$tmpg = 0;
$intersections = array();
$x = (float) $from;
do {
try {
$tmpf = $f($x);
$tmpg = $g($x);
} catch (Exception $e) {
if ($discardExceptions) {
$ignoreActiveIteration = true;
} else {
throw $e;
}
}
if ($ignoreActiveIteration) {
// reset for next iteration
$ignoreActiveIteration = false;
} elseif (abs($tmpf - $tmpg) <= $epsilon) {
$intersections[] = $x;
break;
}
$x += $step;
if ($x > $to) {
break;
}
} while (true);
return $intersections;
}
$f = function ($x) { return pow($x, 2) - 1; };
$g = function ($x) { return sin($x); };
var_dump(findIntersection($f, $g, -2, 2, 0.001, 0.001));
// http://www.wolframalpha.com/input/?i=x^2+-1+%3D+sin+x
// array(1) {
// [0]=>
// float(-0.63700000000011)
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment