Я пытаюсь преобразовать вычисления, введенные пользователями с десятичными результатами во фракции. Например, 66.6666666667 – 66 2/3. Любые указатели? Заранее спасибо
Дальнейшие фракции могут быть использованы для нахождения рациональных приближений к действительным числам, которые являются «лучшими» в строгом смысле. Вот функция PHP, которая находит рациональную аппроксимацию данного (положительного) числа с плавающей запятой с относительной погрешностью менее $tolerance
:
<?php function float2rat($n, $tolerance = 1.e-6) { $h1=1; $h2=0; $k1=0; $k2=1; $b = 1/$n; do { $b = 1/$b; $a = floor($b); $aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux; $aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux; $b = $b-$a; } while (abs($n-$h1/$k1) > $n*$tolerance); return "$h1/$k1"; } printf("%s\n", float2rat(66.66667)); # 200/3 printf("%s\n", float2rat(sqrt(2))); # 1393/985 printf("%s\n", float2rat(0.43212)); # 748/1731
Я написал больше об этом алгоритме и почему он работает, и даже демонстрацию JavaScript здесь: http://jonisalonen.com/2012/converting-decimal-numbers-to-ratios/
Преобразованный код Python в ответ от @ APerson241 на PHP
<?php function farey($v, $lim) { // No error checking on args. lim = maximum denominator. // Results are array(numerator, denominator); array(1, 0) is 'infinity'. if($v < 0) { list($n, $d) = farey(-$v, $lim); return array(-$n, $d); } $z = $lim - $lim; // Get a "zero of the right type" for the denominator list($lower, $upper) = array(array($z, $z+1), array($z+1, $z)); while(true) { $mediant = array(($lower[0] + $upper[0]), ($lower[1] + $upper[1])); if($v * $mediant[1] > $mediant[0]) { if($lim < $mediant[1]) return $upper; $lower = $mediant; } else if($v * $mediant[1] == $mediant[0]) { if($lim >= $mediant[1]) return $mediant; if($lower[1] < $upper[1]) return $lower; return $upper; } else { if($lim < $mediant[1]) return $lower; $upper = $mediant; } } } // Example use: $f = farey(66.66667, 10); echo $f[0], '/', $f[1], "\n"; # 200/3 $f = farey(sqrt(2), 1000); echo $f[0], '/', $f[1], "\n"; # 1393/985 $f = farey(0.43212, 2000); echo $f[0], '/', $f[1], "\n"; # 748/1731
В этом случае весьма полезны фракции Farey.
Они могут быть использованы для преобразования любого десятичного числа в дроби с наименьшим возможным знаменателем.
Извините – у меня нет прототипа в PHP, поэтому вот в Python:
def farey(v, lim): """No error checking on args. lim = maximum denominator. Results are (numerator, denominator); (1, 0) is 'infinity'.""" if v < 0: n, d = farey(-v, lim) return (-n, d) z = lim - lim # Get a "zero of the right type" for the denominator lower, upper = (z, z+1), (z+1, z) while True: mediant = (lower[0] + upper[0]), (lower[1] + upper[1]) if v * mediant[1] > mediant[0]: if lim < mediant[1]: return upper lower = mediant elif v * mediant[1] == mediant[0]: if lim >= mediant[1]: return mediant if lower[1] < upper[1]: return lower return upper else: if lim < mediant[1]: return lower upper = mediant
Основываясь на ответе @ Joni, вот что я использовал, чтобы вытащить все число.
function convert_decimal_to_fraction($decimal){ $big_fraction = float2rat($decimal); $num_array = explode('/', $big_fraction); $numerator = $num_array[0]; $denominator = $num_array[1]; $whole_number = floor( $numerator / $denominator ); $numerator = $numerator % $denominator; if($numerator == 0){ return $whole_number; }else if ($whole_number == 0){ return $numerator . '/' . $denominator; }else{ return $whole_number . ' ' . $numerator . '/' . $denominator; } } function float2rat($n, $tolerance = 1.e-6) { $h1=1; $h2=0; $k1=0; $k2=1; $b = 1/$n; do { $b = 1/$b; $a = floor($b); $aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux; $aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux; $b = $b-$a; } while (abs($n-$h1/$k1) > $n*$tolerance); return "$h1/$k1"; }
Вот мой подход к этой проблеме, отлично работает с номерами рациональных чисел. http://www.carlosabundis.com/2014/03/25/converting-decimals-to-fractions-with-php-v2/
function dec2fracso($dec){ //Negative number flag. $num=$dec; if($num<0){ $neg=true; }else{ $neg=false; } //Extracts 2 strings from input number $decarr=explode('.',(string)$dec); //Checks for divided by zero input. if($decarr[1]==0){ $decarr[1]=1; $fraccion[0]=$decarr[0]; $fraccion[1]=$decarr[1]; return $fraccion; } //Calculates the divisor before simplification. $long=strlen($decarr[1]); $div="1"; for($x=0;$x<$long;$x++){ $div.="0"; } //Gets the greatest common divisor. $x=(int)$decarr[1]; $y=(int)$div; $gcd=gmp_strval(gmp_gcd($x,$y)); //Calculates the result and fills the array with the correct sign. if($neg){ $fraccion[0]=((abs($decarr[0])*($y/$gcd))+($x/$gcd))*(-1); }else{ $fraccion[0]=(abs($decarr[0])*($y/$gcd))+($x/$gcd); } $fraccion[1]=($y/$gcd); return $fraccion; }