如何将小数简化为最小可能的分数?

时间:2012-12-22 10:42:25

标签: javascript function math

例如,如果我的函数被称为getlowestfraction(),那么这就是我期望的:

getlowestfraction(0.5) // returns 1, 2 or something along the lines of that

另一个例子:

getlowestfraction(0.125) // returns 1, 8 or something along the lines of that

5 个答案:

答案 0 :(得分:6)

使用Continued Fractions可以有效地创建(有限或无限)分数 h n / k n 的序列给定实数 x 的任意良好近似值。

如果 x 是有理数,则过程在某个时刻停止, h n / k n == x < / em>的。如果 x 不是有理数,则序列 h n / k n ,n = 0,1,2,.. 。非常快地收敛到 x

连续分数算法仅产生减少的分数(分母和分母是相对素数),分数在 有些人认为给定实数的“最合理的近似值”。

我不是一个JavaScript人(通常使用C编程),但我尝试使用以下JavaScript函数实现该算法。如果有愚蠢的错误,请原谅我。但我检查了这个功能,它似乎工作正常。

function getlowestfraction(x0) {
    var eps = 1.0E-15;
    var h, h1, h2, k, k1, k2, a, x;

    x = x0;
    a = Math.floor(x);
    h1 = 1;
    k1 = 0;
    h = a;
    k = 1;

    while (x-a > eps*k*k) {
        x = 1/(x-a);
        a = Math.floor(x);
        h2 = h1; h1 = h;
        k2 = k1; k1 = k;
        h = h2 + a*h1;
        k = k2 + a*k1;
    }

    return h + "/" + k;
}

当有理逼近精确或具有给定精度eps = 1.0E-15时,循环停止。当然,您可以根据需要调整精度。 (while条件来源于连续分数理论。)

示例(使用while循环的迭代次数):

getlowestfraction(0.5)     = 1/2               (1 iteration)
getlowestfraction(0.125)   = 1/8               (1 iteration)
getlowestfraction(0.1+0.2) = 3/10              (2 iterations)
getlowestfraction(1.0/3.0) = 1/3               (1 iteration)
getlowestfraction(Math.PI) = 80143857/25510582 (12 iterations)

请注意,此算法会将1/3作为x = 1.0/3.0的近似值。将x重复乘以10的幂并取消常见因子会得到类似3333333333/10000000000的内容。

以下是不同精度的示例:

  • 使用eps = 1.0E-15,您获得getlowestfraction(0.142857) = 142857/1000000
  • 使用eps = 1.0E-6,您获得getlowestfraction(0.142857) = 1/7

答案 1 :(得分:1)

你可以保持乘以10,直到你的分子和分母有整数值,然后使用this question的答案将分数减少到最简单的条件。

答案 2 :(得分:1)

请尝试使用此程序:

function toFrac(number) {
    var fractional = number % 1;

    if (fractional) {
        var real = number - fractional;
        var exponent = String(fractional).length - 2;
        var denominator = Math.pow(10, exponent);
        var mantissa = fractional * denominator;
        var numerator = real * denominator + mantissa;
        var gcd = GCD(numerator, denominator);
        denominator /= gcd;
        numerator /= gcd;
        return [numerator, denominator];
    } else return [number, 1];
}

function gcd(numerator, denominator) {
    do {
        var modulus = numerator % denominator;
        numerator = denominator;
        denominator = modulus;
    } while (modulus);

    return numerator;
}

然后您可以按如下方式使用它:

var start = new Date;
var PI = toFrac(Math.PI);
var end = new Date;

alert(PI);
alert(PI[0] / PI[1]);
alert(end - start + " ms");

您可以在此处查看演示:http://jsfiddle.net/MZaK9/1/

答案 3 :(得分:0)

只是摆弄代码,并自己得到答案:

function getlowestfraction (num) {
  var i = 1;
  var mynum = num;
  var retnum = 0;
  while (true) {
    if (mynum * i % 1 == 0) {
      retnum = mynum * i;
      break;
    }
    // For exceptions, tuned down MAX value a bit
    if (i > 9000000000000000) {
      return false;
    }
    i++;
  }
  return retnum + ", " + i;
}

如果有人需要它。

P.S:我不是要展示我的专业知识或知识范围。我确实花了很长时间在JSFiddle试图解决这个问题(不管怎么说都不是很长时间)。

答案 4 :(得分:0)

为简单起见,假设数字为x = 0 . ( a_1 a_2 ... a_k ) ( a_1 a_2 ... a_k ) ....(请记住,前几个数字可能不适合重复模式,我们需要一种方法来确定k是什么)。如果b是基础,那么

b ^ k * x - x = ( b ^ k - 1 ) * x

一方面,但

b ^ k * x - x = ( a_1 a_2 ... a_k )
另一方面,

(确切地说,这是一个整数)。

所以

x = ( a_1 ... a_k ) / ( b ^ k - 1 )

现在你可以使用Euclid的算法来获取gcd并将其除去以获得减少的分数。

你仍然需要弄清楚如何确定重复序列。这个问题应该有答案。编辑 - 一个答案:如果与模式\1匹配,则为/([0-9]+)\1+$/的长度(您可能希望在匹配舍入的bc之前丢弃最后一个数字)。如果没有匹配,则没有比“平凡”表示更好的“答案”(x * base ^ precision / base ^ precision)。

N.B。这个答案会对您对答案的期望做出一些假设,可能不适合您的需求。但它是从重复的十进制表示中再现分数的“教科书”方式 - see e.g. here