PageRenderTime 43ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/php/Math/Fibonacci.php

https://bitbucket.org/adarshj/convenient_website
PHP | 489 lines | 197 code | 23 blank | 269 comment | 54 complexity | b2bf381e2478ff2a593a8d7d354c0045 MD5 | raw file
Possible License(s): Apache-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-2-Clause, GPL-2.0, LGPL-3.0
  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2002 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Jesus M. Castagnetto <jmcastagnetto@php.net> |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Fibonacci.php,v 1.1 2003/01/02 01:56:59 jmcastagnetto Exp $
  20. //
  21. include_once 'PEAR.php';
  22. include_once 'Math/IntegerOp.php';
  23. include_once 'Math/Fibonacci/_fibonacciTable.php';
  24. // PHI and phi are the roots of: x^2 - x - 1 = 0
  25. // PHI is called the golden ratio. Also phi = -1/PHI = 1 - PHI
  26. /**
  27. * MATH_PHI: The golden ratio = (1 + sqrt(5))/2
  28. */
  29. define ('MATH_PHI', (1 + sqrt(5))/2);
  30. /**
  31. * MATH_PHI_x100000: (int) (MATH_PHI * 100000)
  32. */
  33. define ('MATH_PHI_x100000', intval(MATH_PHI * 100000));
  34. /**
  35. * MATH_phi: The reciprocal of PHI = (1 - sqrt(5))/2
  36. */
  37. define ('MATH_phi', (1 - sqrt(5))/2);
  38. /**
  39. * MATH_phi_x100000: (int) (MATH_phi * 100000)
  40. */
  41. define ('MATH_phi_x100000', intval(MATH_phi * 100000));
  42. /**
  43. * MATH_SQRT5_x100000: (int) (sqrt(5) * 100000)
  44. */
  45. define ('MATH_SQRT5_x100000', intval(sqrt(5) * 100000));
  46. /**
  47. * MATH_LNSQRT5: ln(sqrt(5))
  48. */
  49. define ('MATH_LNSQRT5', log(sqrt(5)));
  50. /**
  51. * MATH_LNPHI: ln(PHI)
  52. */
  53. define ('MATH_LNPHI', log(MATH_PHI));
  54. /**
  55. * Math_Fibonacci: class to calculate, validate, and decompose into Fibonacci numbers
  56. *
  57. * Examples:
  58. * <pre>
  59. * include_once 'Math/Fibonacci.php';
  60. *
  61. * $idx = 20;
  62. * echo "Calculate F($idx), fast equation = ";
  63. * $fib =& Math_Fibonacci::term($idx);
  64. * echo $fib->toString()."\n";
  65. * // Calculate F(20), fast equation = 6765
  66. *
  67. * $idx = 55;
  68. * echo "Calculate F($idx), lookup table = ";
  69. * $fib =& Math_Fibonacci::term($idx);
  70. * echo $fib->toString()."\n";
  71. * // Calculate F(55), lookup table = 139583862445
  72. *
  73. * $idx = 502;
  74. * echo "Calculate F($idx), addition loop = ";
  75. * $fib = Math_Fibonacci::term($idx);
  76. * echo $fib->toString()."\n";
  77. * // Calculate F(502), addition loop = 365014740723634211012237077906479355996081581501455497852747829366800199361550174096573645929019489792751
  78. *
  79. * echo "\nSeries from F(0) to F(10):\n";
  80. * $series = Math_Fibonacci::series(10);
  81. * foreach ($series as $n=>$fib) {
  82. * echo "n = $n, F(n) = ".$fib->toString()."\n";
  83. * }
  84. * // Series from F(0) to F(10):
  85. * // n = 0, F(n) = 0
  86. * // n = 1, F(n) = 1
  87. * // n = 2, F(n) = 1
  88. * // n = 3, F(n) = 2
  89. * // n = 4, F(n) = 3
  90. * // n = 5, F(n) = 5
  91. * // n = 6, F(n) = 8
  92. * // n = 7, F(n) = 13
  93. * // n = 8, F(n) = 21
  94. * // n = 9, F(n) = 34
  95. * // n = 10, F(n) = 55
  96. *
  97. * echo "\nand now from F(11) to F(19):\n";
  98. * $series = Math_Fibonacci::series(11, 19);
  99. * foreach ($series as $n=>$fib) {
  100. * echo "n = $n, F(n) = ".$fib->toString()."\n";
  101. * }
  102. * // and now from F(11) to F(19):
  103. * // n = 11, F(n) = 89
  104. * // n = 12, F(n) = 144
  105. * // n = 13, F(n) = 233
  106. * // n = 14, F(n) = 377
  107. * // n = 15, F(n) = 610
  108. * // n = 16, F(n) = 987
  109. * // n = 17, F(n) = 1597
  110. * // n = 18, F(n) = 2584
  111. * // n = 19, F(n) = 4181
  112. *
  113. * echo "\nChecking if 26 and 4181 are Fibonacci numbers\n";
  114. * $verb = Math_Fibonacci::isFibonacci(new Math_Integer(26)) ? 'is' : 'is not';
  115. * echo "26 $verb a Fibonacci number\n";
  116. * // 26 is not a Fibonacci number
  117. * $verb = Math_Fibonacci::isFibonacci(new Math_Integer(4181)) ? 'is' : 'is not';
  118. * echo "4181 $verb a Fibonacci number\n";
  119. * // 4181 is a Fibonacci number
  120. *
  121. * echo "\nDecompose 34512\n";
  122. * $decarr = Math_Fibonacci::decompose(new Math_Integer(34512));
  123. * foreach ($decarr as $fib) {
  124. * $index = Math_Fibonacci::getIndexOf($fib);
  125. * echo "F(".$index->toString().") = ".$fib->toString()."\n";
  126. * }
  127. * // Decompose 34512
  128. * // F(23) = 28657
  129. * // F(19) = 4181
  130. * // F(17) = 1597
  131. * // F(10) = 55
  132. * // F(8) = 21
  133. * // F(2) = 1
  134. *
  135. * echo "\nF(n) closest to 314156 is: ";
  136. * $fib = Math_Fibonacci::closestTo(new Math_Integer(314156));
  137. * echo $fib->toString()."\n\n";
  138. * // F(n) closest to 314156 is: 317811
  139. *
  140. * echo 'The index for 1597 is : ';
  141. * $idx = Math_Fibonacci::getIndexOf(new Math_Integer(1597));
  142. * echo $idx->toString()."\n\n";
  143. * // The index for 1597 is : 17
  144. *
  145. * $bigint = '3141579834521345220291';
  146. * echo "Finding the Fibonacci numbers that add up to $bigint\n";
  147. * $series = Math_Fibonacci::decompose(new Math_Integer($bigint));
  148. * foreach ($series as $fib) {
  149. * $index = Math_Fibonacci::getIndexOf($fib);
  150. * echo "F(".$index->toString().") = ".$fib->toString()."\n";
  151. * }
  152. * // Finding the Fibonacci numbers that add up to 3141579834521345220291
  153. * // F(104) = 2427893228399975082453
  154. * // F(101) = 573147844013817084101
  155. * // F(98) = 135301852344706746049
  156. * // F(91) = 4660046610375530309
  157. * // F(86) = 420196140727489673
  158. * // F(83) = 99194853094755497
  159. * // F(81) = 37889062373143906
  160. * // F(79) = 14472334024676221
  161. * // F(76) = 3416454622906707
  162. * // F(74) = 1304969544928657
  163. * // F(71) = 308061521170129
  164. * // F(68) = 72723460248141
  165. * // F(63) = 6557470319842
  166. * // F(60) = 1548008755920
  167. * // F(57) = 365435296162
  168. * // F(53) = 53316291173
  169. * // F(51) = 20365011074
  170. * // F(49) = 7778742049
  171. * // F(44) = 701408733
  172. * // F(37) = 24157817
  173. * // F(31) = 1346269
  174. * // F(26) = 121393
  175. * // F(20) = 6765
  176. * // F(16) = 987
  177. * // F(13) = 233
  178. * // F(8) = 21
  179. * // F(6) = 8
  180. * // F(3) = 2
  181. *
  182. * </pre>
  183. *
  184. * @author Jesus M. Castagnetto <jmcastagnetto@php.net>
  185. * @version 0.8
  186. * @access public
  187. * @package Math_Fibonacci
  188. */
  189. class Math_Fibonacci {/*{{{*/
  190. /**
  191. * Calculates a Fibonacci number using the (exact) golden ratio equation:
  192. * F(n) = (PHI^n - phi^n)/sqrt(5) [Lucas formula]
  193. * for terms from [0,46]
  194. * from [47,500] it uses a lookup table
  195. * from then on uses the recursive addition
  196. *
  197. * @param mixed $n the index of the Fibonacci number to calculate, as an integer or a Math_Integer number
  198. * @return mixed numeric on success, PEAR_Error otherwise
  199. * @access public
  200. */
  201. function &term($n) {/*{{{*/
  202. if (Math_IntegerOp::isMath_Integer($n)) {
  203. $idx =& $n;
  204. } elseif (is_numeric($n) && $n >= 0) {
  205. $idx =& new Math_Integer($n);
  206. } else {
  207. return PEAR::raiseError("The parameter $n is not a Math_Integer object");
  208. }
  209. $table =& $GLOBALS['_fibonacciTable'];
  210. // shortcut and check if it is already a cached value
  211. if (isset($table[$idx->toString()])) {
  212. return new Math_Integer($table[$idx->toString()]);
  213. }
  214. // from index [0,46] use the fast algorithm
  215. $cmp_0 = Math_IntegerOp::compare($idx, new Math_Integer(0));
  216. $cmp_46 = Math_IntegerOp::compare($idx, new Math_Integer(46));
  217. if ( ($cmp_0 >= 0) && ($cmp_46 <= 0) ) {
  218. $val = intval($idx->toString());
  219. $fn = (pow(MATH_PHI, $val) - pow(MATH_phi, $val))/sqrt(5);
  220. // add to lookup table
  221. $table[$val] = $fn;
  222. return new Math_Integer(strval($fn));
  223. }
  224. // from [47,500] use the lookup table
  225. $cmp_500 = Math_IntegerOp::compare($idx, new Math_Integer(500));
  226. if ( ($cmp_46 > 0) && ($cmp_500 <= 0) ) {
  227. return new Math_Integer($table[$idx->toString()]);
  228. } else {
  229. // calculate and cache the values
  230. $a = new Math_Integer($table['499']);
  231. $b = new Math_Integer($table['500']);
  232. $pos = new Math_Integer('501');
  233. $one = new Math_Integer('1');
  234. while (Math_IntegerOp::compare($pos,$idx) <= 0) {
  235. $c = Math_IntegerOp::add($a, $b);
  236. $table[$pos->toString()] = $c->toString();
  237. $a = $b;
  238. $b = $c;
  239. $pos = Math_IntegerOp::add($pos, $one);
  240. }
  241. return $c;
  242. }
  243. }/*}}}*/
  244. /**
  245. * Returns a series of Fibonacci numbers using the given limits.
  246. * Method accepts two parameters, of which the second one is
  247. * optional. If two parameters are passed, the first one will be
  248. * lower bound and the second one the upper bound. If only one
  249. * parameter is passed, it will be the upper bound, and the lower
  250. * bound will be 0 (zero).
  251. *
  252. * @param integer $idx1 the lower index for the series (if two parameters) were passed, or the upper index if only one was given.
  253. * @param optional integer $idx2 the upper index for the series
  254. * @return mixed on success, an array of integers where the keys correspond to the indexes of the corresponding Fibonacci numbers, or PEAR_Error othewise
  255. * @access public
  256. */
  257. function series($idx1, $idx2=null) {/*{{{*/
  258. if (is_integer($idx1) && $idx1 > 0) {
  259. if ($idx2 == null) {
  260. $lower_bound = 0;
  261. $upper_bound = $idx1;
  262. } elseif (is_integer($idx2)) {
  263. if ($idx2 < 0) {
  264. return PEAR::raiseError("Upper limit $idx2 cannot be negative");
  265. } elseif ($idx2 < $idx1) {
  266. return PEAR::raiseError("Upper limit cannot be smaller than lower limit");
  267. } else {
  268. $lower_bound = $idx1;
  269. $upper_bound = $idx2;
  270. }
  271. }
  272. $fibSeries = array();
  273. for ($i=$lower_bound; $i <= $upper_bound; $i++) {
  274. $fibSeries[$i] =& Math_Fibonacci::term($i);
  275. }
  276. return $fibSeries;
  277. } else {
  278. return PEAR::raiseError("The parameter $idx1 is not a valid integer");
  279. }
  280. }/*}}}*/
  281. /**
  282. * Determines if a particular integer is part of the Fibonacci series
  283. *
  284. * @param integer $num
  285. * @return mixed TRUE if it is a Fibonacci number, FALSE if not, PEAR_Error if the parameter was not an integer
  286. * @access public
  287. * @see Math_Fibonacci::term
  288. */
  289. function isFibonacci($num) {/*{{{*/
  290. if (!Math_IntegerOp::isMath_Integer($num)) {
  291. return PEAR::raiseError('Not a valid Math_Integer object');
  292. }
  293. $n = Math_Fibonacci::_estimateN($num);
  294. $intcalc =& Math_Fibonacci::term($n);
  295. $cmp = Math_IntegerOp::compare($num, $intcalc);
  296. return ($cmp == 0);
  297. }/*}}}*/
  298. /**
  299. * Decomposes an integer into a sum of Fibonacci numbers
  300. *
  301. * @param integer $num
  302. * @return mixed an array of Fibonacci numbers on success, PEAR_Error otherwise
  303. * @access public
  304. */
  305. function decompose($num) {/*{{{*/
  306. if (!Math_IntegerOp::isMath_Integer($num)) {
  307. return PEAR::raiseError('Not a valid Math_Integer object');
  308. }
  309. $err = Math_Fibonacci::_recDecompose($num, &$sum);
  310. if (PEAR::isError($err)) {
  311. return $err;
  312. }
  313. $check = new Math_Integer(0);
  314. foreach($sum as $fib) {
  315. if (PEAR::isError($fib)) {
  316. return $fib;
  317. } else {
  318. $check = Math_IntegerOp::add($check, $fib);
  319. }
  320. }
  321. $int =& new Math_Integer($num);
  322. if (Math_IntegerOp::compare($num,$check) == 0) {
  323. return $sum;
  324. } else {
  325. $numstr = $num->toString();
  326. $sumsrt = $check->toString;
  327. return PEAR::raiseError("Number and sum do not match: $numstr != $sumstr");
  328. }
  329. }/*}}}*/
  330. /**
  331. * Finds the Fibonacci number closest to an given integer
  332. *
  333. * @param integer $num
  334. * @return mixed a Fibonacci number (integer) on success, PEAR_Error otherwise
  335. * @access public
  336. */
  337. function closestTo($num) {/*{{{*/
  338. if (!Math_IntegerOp::isMath_Integer($num)) {
  339. return PEAR::raiseError("Invalid parameter: not a Math_Integer object");
  340. }
  341. $n = Math_Fibonacci::_estimateN($num);
  342. $fib1 =& Math_Fibonacci::term($n);
  343. $cmp = Math_IntegerOp::compare($fib1,$num);
  344. if ($cmp == 0) {
  345. return $fib1;
  346. } elseif ($cmp == 1) { // overshoot, see n - 1
  347. $new_n = Math_IntegerOp::sub($n, new Math_Integer(1));
  348. } else { // undeshoot, try n + 1
  349. $new_n = Math_IntegerOp::add($n, new Math_Integer(1));
  350. }
  351. $fib2 = Math_Fibonacci::term($new_n);
  352. $d1 = Math_IntegerOp::abs(Math_IntegerOp::sub($fib1, $num));
  353. $d2 = Math_IntegerOp::abs(Math_IntegerOp::sub($fib2, $num));
  354. $cmp = Math_IntegerOp::compare($d1, $d2);
  355. if ($cmp == -1 || $cmp == 0) {
  356. return $fib1;
  357. } else {
  358. return $fib2;
  359. }
  360. }/*}}}*/
  361. /**
  362. * Gets the index in the Fibonacci series of a given number.
  363. * If the integer given is not a Fibonacci number a PEAR_Error object
  364. * will be returned.
  365. *
  366. * @param integer $num the Fibonacci number
  367. * @return mixed the index of the number in the series on success, PEAR_Error otherwise.
  368. * @access public
  369. */
  370. function getIndexOf($num) {/*{{{*/
  371. if (!Math_IntegerOp::isMath_Integer($num)) {
  372. return PEAR::raiseError("Invalid parameter: not a Math_Integer object");
  373. }
  374. // check in the lookup table
  375. $n = Math_Fibonacci::_estimateN($num);
  376. $fibn = Math_Fibonacci::term($n);
  377. $cmp = Math_IntegerOp::compare($num, $fibn);
  378. if ($cmp == 0) {
  379. return $n;
  380. } else {
  381. return PEAR::raiseError("Integer $num is not a Fibonacci number");
  382. }
  383. }/*}}}*/
  384. /**
  385. * Recursive utility method used by Math_Fibonacci::decompose()
  386. *
  387. * @param integer $num
  388. * @param array $sum array of Fibonacci numbers
  389. * @return mixed null on success, PEAR_Error otherwise
  390. * @access private
  391. */
  392. function _recDecompose($num, &$sum) {/*{{{*/
  393. if (!Math_IntegerOp::isMath_Integer($num)) {
  394. return PEAR::raiseError('Not a valid Math_Integer object');
  395. }
  396. if (!is_array($sum)) {
  397. $sum = array();
  398. }
  399. $n = Math_Fibonacci::_estimateN($num);
  400. if (PEAR::isError($n)) {
  401. return $n;
  402. }
  403. $fibn = Math_Fibonacci::term($n);
  404. if (PEAR::isError($fibn)) {
  405. return $fibn;
  406. }
  407. $cmp = Math_IntegerOp::compare($fibn, $num);
  408. if ($cmp == 0) {
  409. $sum[] = $fibn;
  410. return null;
  411. } elseif ($cmp == -1) {
  412. $sum[] = $fibn;
  413. $newnum = Math_IntegerOp::sub($num, $fibn);
  414. Math_Fibonacci::_recDecompose($newnum, &$sum);
  415. } elseif ($cmp == 1) {
  416. $n_1 = Math_IntegerOp::sub($n, new Math_Integer(1));
  417. if (PEAR::isError($n_1)) {
  418. return $n_1;
  419. }
  420. $fibn_1 = Math_Fibonacci::term($n_1);
  421. if (PEAR::isError($fibn_1)) {
  422. return $fibn_1;
  423. }
  424. $sum[] = $fibn_1;
  425. $newnum = Math_IntegerOp::sub($num, $fibn_1);
  426. Math_Fibonacci::_recDecompose($newnum, &$sum);
  427. }
  428. }/*}}}*/
  429. /**
  430. * Estimates the approximate index for a given number
  431. * It uses the approximate formula:
  432. * F(n) ~ (PHI^n)/sqrt(5), where '~' means the 'closest integer to'
  433. * This equation is based on the relation: phi = -1/PHI
  434. * Which turns Lucas' formula into:
  435. * F(n) = (PHI^2n + 1)/(PHI^n * sqrt(5))
  436. * From which we get the formula above, after making the approximation:
  437. * (PHI^2n + 1) -> (PHI^2n)
  438. *
  439. * @param integer $num
  440. * @return integer the approximate index
  441. * @access private
  442. */
  443. function &_estimateN(&$num) {/*{{{*/
  444. if (Math_IntegerOp::isMath_Integer($num)) {
  445. $f = $num->toString();
  446. } else {
  447. $f = $num;
  448. }
  449. return Math_Fibonacci::_closestInt((log($f) + MATH_LNSQRT5) / MATH_LNPHI);
  450. }/*}}}*/
  451. /**
  452. * Finds the closest integer to a given number
  453. *
  454. * @param numeric $num
  455. * @return integer
  456. * @access private
  457. */
  458. function &_closestInt($num) {/*{{{*/
  459. $f = floor($num);
  460. $c = ceil($num);
  461. return new Math_Integer(($num - $f) < ($c - $num) ? $f : $c);
  462. }/*}}}*/
  463. }/* End of Math_Fibonacci }}}*/
  464. // vim: ts=4:sw=4:et:
  465. // vim6: fdl=1:
  466. ?>