十进制到分数转换

时间:2012-02-21 22:37:00

标签: algorithm

我接受了以下问题的采访:

将小数更改为最接近的分数的程序。

实施例: 0.12345 => 20000分之2649

0.34 => 17/50

解决此问题的最佳方法是什么?

8 个答案:

答案 0 :(得分:6)

我最近提出的方法是摆脱小数:

0.12345 = 0.12345 / 1 = 12345/100000

然后找到Greatest common divisor并将其除以。

答案 1 :(得分:2)

十进制数是分母为十的幂(对于任何数字基数同样)的分数。所以0.34是34/100。现在只需取消公因子,即将分子和分母除以它们最大的公约数。

您可以使用标准算法找到GCD,我留给您查找。

答案 2 :(得分:2)

http://homepage.smc.edu/kennedy_john/DEC2FRAC.PDF

简而言之:

  

设X为小数值。 Z 1 == X,D 0 == 0和D 1 == 1.

     

Z i + 1 == 1 /(Z i - IntPart(Z i ))

     

D i + 1 == D i * IntPart(Z i + 1 )+ D i-1

     

N i + 1 == Round(X * D i + 1

当您通过从定点Z 1 迭代这些Z,D和N系列找到时,产生N i 的系列的索引i并且D i 使得N i / D i == X到期望的精度,你就完成了。

了解十进制值最好存储为decimal类型,并且N / D的计算也应该产生十进制类型,以避免浮点舍入错误。

解决方案:

public struct Fraction
{
    public int Numerator { get; private set; }
    public int Denominator { get; private set; }
    public decimal DecimalValue { get { return ((decimal)Numerator) / Denominator; } }
    public override string ToString()
    {
        return Numerator.ToString() + "/" + Denominator.ToString();
    }

    public static Fraction FromDecimal(decimal x)
    {
        decimal z = x;
        decimal dPrev = 0;
        decimal dCur = 1;
        decimal dTemp = dCur;
        decimal n = 0;

        while (n / dCur != x)
        {
            z = 1 / (z - (int)z);
            dTemp = dCur;
            dCur = (dCur * (int)z) + dPrev;
            dPrev = dTemp;
            n = Math.Round(x * dCur);
        }

        return new Fraction {Numerator = (int) n, Denominator = (int) dCur};
    }
}

该算法能够找到由于精度限制而人为终止的有理数的“真实”小数值(即.3333333为1/3,而不是3333333/10000000);它通过评估每个候选分数来产生一个会受到相同精度限制的值来做到这一点。它实际上取决于那个;为这个算法提供 pi 的真值(如果可以的话)会导致无限循环。

答案 3 :(得分:1)

天真的解决方案......

  1. 0.12345 = 12345/100000
  2. 找到最大的公约数
  3. 将分数的两个部分与GCD
  4. 分开
  5. 利润

答案 4 :(得分:1)

首先,通过连续乘以10使分子和分母成为积分。

因此,0.34 / 1变为34/100,0.12345 / 1变为12345/100000

然后使用GCD计算得到这两个数的最大公约数,并将它们除以。

34和100的GCD是2,它给你17/50。 12345和1000000的GCD是5,它给你2469/20000。

C中的递归GCD函数是:

int gcd (int a, int b) {
    return (b == 0) ? a : gcd (b, a%b);
}

答案 5 :(得分:1)

Kerrek SB的回答是正确的,但是使用连续分数很容易找到任何实数的最合理逼近(表示为浮点数或不浮点数) ,对于任何给定的最大分母。 这里描述了该方法的最佳性质:http://en.wikipedia.org/wiki/Continued_fraction#Some_useful_theorems,定理4和5。

示例结果:分母sqrt(2),分母小于或等于23:

findapprox(math.sqrt(2),23)
          (3, 2)  new error frac: 6.0660e-02 
          (7, 5)  new error frac: 1.0051e-02 
        (17, 12)  new error frac: 1.7346e-03 
        (41, 29)  new error frac: 2.9731e-04 
result: (17, 12)

示例:约为23.1234,分母< = 20000:

findapprox(23.1234,20000)
            (185, 8)  new error frac: 6.9194e-05 
          (1688, 73)  new error frac: 4.8578e-06 
          (1873, 81)  new error frac: 2.4560e-06 
         (3561, 154)  new error frac: 1.0110e-06 
         (5434, 235)  new error frac: 1.8403e-07 
        (19863, 859)  new error frac: 3.0207e-08 
       (25297, 1094)  new error frac: 1.5812e-08 
       (45160, 1953)  new error frac: 4.4287e-09 
       (70457, 3047)  new error frac: 2.8386e-09 
      (115617, 5000)  new error frac: 0.0000e+00 
result: (115617, 5000) (exact)

持续的分数具有一些时髦的特征。例如,sqrt(2)可以写成1,2,2 ,, ...表示1 + 1 /(2 + 1 /(2 + 1 / ...)。因此我们可以找到sqrt的最优有理逼近( 2):

rewrap([1]+[2]*30) gives 367296043199 / 259717522849, 
all correct: 1.4142135623730951

这是代码(python)。

# make a+1/(b+1/(c+1/...)) cont fraction of a float
# no special care taken to accumulation of float truncation errors.
def contfrac(v):
    nums=None
    while nums==None or len(nums)<20:
        if not nums:
            nums=[]
        vi=int(v)
        v=v-vi
        nums.append(vi)
        if v<=0 : 
            return nums
        v=1.0/v
    return nums


# make tuple representing a rational number based on a cont f list 
# this is exact
def rewrap(v):
    rat=(0,1)
    first=1
    for k in reversed(v):
        if first:
            first=0
            rat=(k,1)
        else:            
            rat=(k*rat[0]+rat[1],rat[0])        
    return rat

# float evaluate a ratio
def makefloat(v):
    return v[0]*1.0/v[1]

# find the best rational approximation with denominator <=maxdenom
def findapprox(flt,maxdenom):
    flt=1.0*flt
    cf=contfrac(flt)
    best=(cf[0],1)
    errfrac=1e9
    for i in range(2,len(cf)):
        new=rewrap(cf[:i])        
        newerrfrac=abs((flt-makefloat(new))/flt)
        print "%20s"%(str(new)),
        print(" new error frac: %5.4e " %(newerrfrac))
        if new[1]>maxdenom or newerrfrac>errfrac:
            return best
        best=new
        errfrac=newerrfrac
        if newerrfrac ==0: 
            return best
    return best

答案 6 :(得分:0)

  1. 摆脱点:(0.a 1 .. a n * 10 n )/(1 * 10 n
  2. x = GCD 1 .. a n ,10 n
  3. (a 1 .. a n / x)/(10 n / x)
  4. foo(double a) {
        int digits = (int)log(a, 10);
        int x = GCD((int)(a * 10^digits) , 10^digits);
        return ((int)(a * 10^digits) / x) + " / " + (10^digits / x);    
    }
    

答案 7 :(得分:0)

首先,将您的数字转换为明显的分数

0.34 => 34/100

0.12345 => 12345/100000

现在减少分数。您需要找到分子的GCD和分母。 34和100的GDC是2.将两个数字除以2得到17/50。

请参阅维基百科上的Greatest common divisor。您还可以找到GCD算法的简要说明。