Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770
Форумы портала PHP.SU :: Версия для печати :: Длинная арифметика
Форумы портала PHP.SU » PHP » Пользовательские функции » Длинная арифметика

Страниц (1): [1]
 

1. EuGen - 23 Января, 2013 - 16:15:44 - перейти к сообщению
Ниже привожу код, оформленный в класс, который позволяет проводить базовые операции над целочисленными операндами с получением целочисленного результата. Суть отличия от обычных, встроенных арифметических операций в том, что поддерживаются операнды практически любой длины.
Код является по сути набором методов, то есть объединение в класс - весьма условно и скорее служит для удобства, нежели преследует архитектурные цели.
PHP:
скопировать код в буфер обмена
  1. class Calculator
  2. {
  3.     public function pow()
  4.     {
  5.         $rgArgs = $this->_get_args(func_get_args());
  6.         if(count($rgArgs)<2)
  7.         {
  8.             return null;
  9.         }
  10.         $sA = $rgArgs[0];
  11.         $sB = $rgArgs[1];
  12.         return $this->_pow_simple($sA, $sB);
  13.     }
  14.    
  15.     public function factorial($sA)
  16.     {
  17.         if(!isset($sA) || !preg_match('/^[-+]{0,1}[0-9]+$/', $sA) || $this->_get_negative($sA))
  18.         {
  19.             return null;
  20.         }
  21.         $sA=(string)$sA;
  22.         return $this->_factorial_simple($sA);
  23.     }
  24.    
  25.     public function abs($sA)
  26.     {
  27.         if(!isset($sA) || !preg_match('/^[-+]{0,1}[0-9]+$/', $sA))
  28.         {
  29.             return null;
  30.         }
  31.         $sA=(string)$sA;
  32.         return $sA[0]=='-'?substr($sA, 1):$sA;
  33.     }
  34.    
  35.     public function mult()
  36.     {
  37.         $rgArgs = $this->_get_args(func_get_args());
  38.         if(!count($rgArgs))
  39.         {
  40.             return null;
  41.         }
  42.         if(count($rgArgs)==1)
  43.         {
  44.             return (string)$rgArgs[0];
  45.         }
  46.         $sResult = '1';
  47.         for($i=0;$i<count($rgArgs);$i++)
  48.         {
  49.             $sResult = $this->_mult_simple($sResult, $rgArgs[$i]);
  50.         }
  51.         return $sResult;
  52.     }
  53.    
  54.     public function sum()
  55.     {
  56.         $rgArgs = $this->_get_args(func_get_args());
  57.         if(!count($rgArgs))
  58.         {
  59.             return null;
  60.         }
  61.         if(count($rgArgs)==1)
  62.         {
  63.             return (string)$rgArgs[0];
  64.         }
  65.         $sResult = '0';
  66.         for($i=0;$i<count($rgArgs);$i++)
  67.         {
  68.             $sResult = $this->_get_negative($rgArgs[$i])?
  69.                     $this->_diff_simple($sResult, $this->abs($rgArgs[$i])):
  70.                     $this->_sum_simple($sResult, $rgArgs[$i]);
  71.         }
  72.         return $sResult;
  73.     }
  74.    
  75.     public function diff()
  76.     {
  77.         $rgArgs = $this->_get_args(func_get_args());
  78.         if(count($rgArgs)<2)
  79.         {
  80.             return null;
  81.         }
  82.         $sA = $rgArgs[0];
  83.         $sB = $rgArgs[1];
  84.         //-|A| -(-|B|) = |B|-|A|
  85.         if($this->_get_negative($sA) && $this->_get_negative($sB))
  86.         {
  87.             return $this->_diff_simple($this->abs($sB), $this->abs($sA));
  88.         }
  89.         //|A| -(-|B|) = |A|+|B|
  90.         if(!$this->_get_negative($sA) && $this->_get_negative($sB))
  91.         {
  92.             return $this->sum($this->abs($sA), $this->abs($sB));
  93.         }
  94.         //(-|A|) -(|B|) = -(|A|+|B|)
  95.         if($this->_get_negative($sA) && !$this->_get_negative($sB))
  96.         {
  97.             return '-'.$this->sum($this->abs($sA), $this->abs($sB));
  98.         }
  99.         //|A| -(|B|) = |A|-|B|
  100.         if(!$this->_get_negative($sA) && !$this->_get_negative($sB))
  101.         {
  102.             return $this->_diff_simple($this->abs($sA), $this->abs($sB));
  103.         }
  104.     }
  105.    
  106.     protected function _pow_simple($sA, $sB)
  107.     {
  108.         if($sB=='0' || $sB=='1')
  109.         {
  110.             return $sA;
  111.         }
  112.         if($sA=='1')
  113.         {
  114.             return '1';
  115.         }
  116.         $sD = $sA;
  117.         $sI = '1';
  118.         while($sI!=$sB)
  119.         {
  120.             $sA = $this->_mult_simple($sA, $sD);
  121.             $sI = $this->_sum_simple($sI, '1');
  122.         }
  123.         return $sA;
  124.     }
  125.    
  126.     protected function _factorial_simple($sA)
  127.     {
  128.         if($sA=='1')
  129.         {
  130.             return '1';
  131.         }
  132.         return $this->mult($this->_factorial_simple($this->diff($sA, '1')), $sA);
  133.     }
  134.    
  135.     protected function _mult_simple($sA, $sB)
  136.     {
  137.         $sSign  = '';
  138.         if($this->_get_negative($sA) ^ $this->_get_negative($sB))
  139.         {
  140.             $sSign = '-';
  141.         }
  142.         $sA     = strrev($this->abs($sA));
  143.         $sB     = strrev($this->abs($sB));
  144.         $rgC    = array_fill(0, strlen($sA)+strlen($sB)-1, 0);
  145.         for($i=0;$i<strlen($sA);$i++)
  146.         {
  147.             for($j=0; $j<strlen($sB);$j++)
  148.             {
  149.                 $iCR    = (int)$sA[$i]*(int)$sB[$j];
  150.                 $k      = $i+$j;//-1;
  151.                 while($iCR>0)
  152.                 {
  153.                     $iCR    += (isset($rgC[$k])?$rgC[$k]:0);
  154.                     $rgC[$k] = $iCR % 10;
  155.                     $iCR     = (int)($iCR/10);
  156.                     $k++;
  157.                 }
  158.             }
  159.         }
  160.         return $sSign.preg_replace('/^[0]+/','', strrev(join('', $rgC)));
  161.     }
  162.    
  163.     protected function _diff_simple($sA, $sB)
  164.     {
  165.         $iMax   = strlen($sA);
  166.         if(strlen($sA)<strlen($sB))
  167.         {
  168.             $iMax   = strlen($sB);
  169.             $sA     = str_repeat('0', $iMax-strlen($sA)).$sA;
  170.         }
  171.         elseif(strlen($sA)>strlen($sB))
  172.         {
  173.             $sB     = str_repeat('0', $iMax-strlen($sB)).$sB;
  174.         }
  175.         $sSign  = '';
  176.         $iC     = 0;
  177.         if($this->_compare_longs($sA, $sB)==-1)
  178.         {
  179.             $sA = $sA ^ $sB;
  180.             $sB = $sA ^ $sB;
  181.             $sA = $sA ^ $sB;
  182.             $sSign = '-';
  183.         }
  184.         for($i=$iMax-1;$i>=0;$i--)
  185.         {
  186.             $iC    += (int)$sA[$i]-(int)$sB[$i]+10;
  187.             $sA[$i] = (string)($iC % 10);
  188.             $iC     = $iC<10?-1:0;
  189.         }
  190.         return $sSign.preg_replace('/^[0]+/','',$sA);
  191.     }
  192.    
  193.     protected function _sum_simple($sA, $sB)
  194.     {
  195.         $iMax   = strlen($sA);
  196.         if(strlen($sA)<strlen($sB))
  197.         {
  198.             $iMax   = strlen($sB);
  199.             $sA     = str_repeat('0', $iMax-strlen($sA)).$sA;
  200.         }
  201.         elseif(strlen($sA)>strlen($sB))
  202.         {
  203.             $sB     = str_repeat('0', $iMax-strlen($sB)).$sB;
  204.         }
  205.         $iC = 0;
  206.         for($i=$iMax-1;$i>=0;$i--)
  207.         {
  208.             $iC    += (int)$sA[$i]+(int)$sB[$i];
  209.             $sA[$i] = (string)($iC % 10);
  210.             $iC     = (int)($iC/10);
  211.         }
  212.         if($iC>0)
  213.         {
  214.             $sA = (string)$iC.$sA;
  215.         }
  216.         return $sA;
  217.     }
  218.    
  219.     protected function _get_negative($sA)
  220.     {
  221.         return $sA[0]=='-';
  222.     }
  223.    
  224.     protected function _compare_longs($sA, $sB)
  225.     {
  226.         $iA = strlen($sA);
  227.         $iB = strlen($sB);
  228.         if($iA<$iB)
  229.         {
  230.             return -1;
  231.         }
  232.         if($iA>$iB)
  233.         {
  234.             return 1;
  235.         }
  236.         for($i=0;$i<$iA;$i++)
  237.         {
  238.             if($sA[$i]>$sB[$i])
  239.             {
  240.                 return 1;
  241.             }
  242.             if($sA[$i]<$sB[$i])
  243.             {
  244.                 return -1;
  245.             }
  246.         }
  247.         return 0;
  248.     }
  249.    
  250.     protected function _get_args($rgArgs)
  251.     {
  252.         if(!count($rgArgs))
  253.         {
  254.             return array();
  255.         }
  256.         if(is_array($rgArgs[0]))
  257.         {
  258.             $rgArgs = $rgArgs[0];
  259.         }
  260.         return $rgArgs;
  261.     }
  262. }

Пример использования: calculator.php:
PHP:
скопировать код в буфер обмена
  1. //includes omitted
  2. $rCalculator = new Calculator();
  3. $mArgs = count(explode(',', $_SERVER['argv'][2]))==1?$_SERVER['argv'][2]:explode(',', $_SERVER['argv'][2]);
  4. var_dump($rCalculator->{$_SERVER['argv'][1]}($mArgs));

Пример вывода:
CODE (bash):
скопировать код в буфер обмена
  1. user@host:/path$ php calculator.php abs -999      
  2. string(3) "999"
  3. user@host:/path$ php calculator.php sum 284000000000000000000000,7876667
  4. string(24) "284000000000000007876667"
  5. user@host:/path$ php calculator.php factorial 1100
  6. string(2870) "53437084880926377034242155822950561183080130768866895408827458818036618050723014259335974351566788327415467514488987033614582476645694464261738910473380885803623188713714545198124600746227142898076872654482044562793852138469677927330572749576948166807421216877498208458769005405366305220797476530746185417476140782853546532719240751414684044603768883655817696461533721419437912125211548097906523004133705326338238190387850483847780372458822923605205280273714625078767863125493425503475250719583192283718603023987580777780035594813037613795902372520221886503677746281612405469606784962303354557187806801326271107816217722875294947071835822552429855506265930472623552940043968375099545722982577206331181572059532678281770892042093628816805623646577311781257247365411215175318229952887274698387234062919360695915639222369179043852637274593590667561348816681249809804769738621480138253259745750162388467598365561726569010392596479459233302432353681098301798797894434267786419982234226196634154442756631212508306232465619001682438953114007137692949367324856071217925158630122800389968328140113994331216849121360746519587860857407128917920142291872364024648458248977997760361360471884983431550636400392657427356942110542723446483822959503637292174818359270147883844245449127993570399680145426326859121741129303986870840568357454719368907061892985017459280119751754150743559728998295556732360250510038817844274754285134199896907638396447772737827107154796119201921536215055057930454911959321972058937160676911357720743058994504672070549484826113247358505388161799865617708367349954370380853219243288318629194580898980578192438937035768656839681865937931209188508228267083150656101893608065513697027997680322239292135637993681781912498871451282367323691586047201433108789851894379984404592216092303916341628274737895100530937654565448323856309779964541012016062668881698149445697033513467332799250377144771926250403307819405123627234925136330333303194372550567266647893455948506593051342066573726745648579107585985953658931045614036038155596569125812922579750260586849926124666596524551569945200247660633286983652662621181138959151246904073105358802979855205418823070789221362661986273869727763296788726872748791940251859679734295741968501303689117417827157419981384831723405209115952963781151997639090113111889675710526793448609726730199591174357535880032929705149172105146812236476055910588649481636927879360190085978752657227077124882768256913401349756816955057706206475508584494415633608256789303048043193593527406679352116588129444309915056549129527927635234094060955675954342173050125247634953207808000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

Кстати, знаменитый "гугол" оказывается не таким уж и большим:
CODE (bash):
скопировать код в буфер обмена
  1. user@host:/path$ php calculator.php pow 100,100                        
  2. string(201) "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

(в принципе, логично, так как 100^100 = (10^2)^100 = 10^(2*100) = 10^200, то есть 1 и 200 нулей) - а, между тем, считается, что это больше, чем число молекул в известной части Вселенной.
upd. Поправка: Гугол - равен 10^100, а не 100^100, таким образом, он еще вдвое "короче" записи выше. Тем не менее, число молекул известной части Вселенной меньше и настоящего "гугола", поскольку имеет порядок 10^79..10^81

Основной упор сделан, разумеется, на алгоритмы длинной арифметики, и, хотя базовая валидация включена для примера в методы abs или factorial, сделано это скорее для наглядности - чтобы при желании её можно было добавить и в остальных случаях.

Сделана поддержка операций:
sum - длинная сумма
diff - длинная разность
abs - длинный модуль
mult - длинное произведение
pow - длинное возведение в степень
factorial - длинный факториал

при этом последние две опираются на предыдущие, и , таким образом, из этого набора можно собрать некоторые другие вспомогательные функции. Длинное деление не возвращает целочисленного результата в общем случае и потому не реализовано.

 

Powered by ExBB FM 1.0 RC1