วิธีการแปลงลอยเป็นเศษส่วนที่มนุษย์อ่านได้?


105

สมมติว่าเรามีเราต้องเอาท์พุท0.33 ถ้าเรามีเราต้องเอาท์พุท1/3
0.42/5

แนวคิดคือการทำให้มนุษย์อ่านได้เพื่อให้ผู้ใช้เข้าใจ " x parts out of y " เป็นวิธีที่ดีขึ้นในการทำความเข้าใจข้อมูล

ฉันรู้ว่าเปอร์เซ็นต์เป็นสิ่งทดแทนที่ดี แต่ฉันสงสัยว่ามีวิธีง่ายๆในการทำเช่นนี้หรือไม่?


ตัวอย่าง.33=> "1/3"เกี่ยวข้องกับฉัน; ผมจะคาดหวัง=>.33 "33/100"ฉันคิดว่าคุณหมายถึง.33...แน่นอน แต่มันทำให้เกิดปัญหากับคำถาม - ก่อนที่เราจะสามารถจัดการกับอัลกอริทึมที่เราต้องตัดสินใจเกี่ยวกับพฤติกรรมที่คาดหวัง คำตอบ Python ของ @ Debilski ใช้.limit_denominator()ค่าเริ่มต้นเป็นตัวหารสูงสุดที่ 10 ^ 7; อาจเป็นค่าเริ่มต้นที่ดีในทางปฏิบัติ แต่สิ่งนี้ยังสามารถทำให้เกิดข้อบกพร่องได้หากคุณไม่ระวังและจะกลับมา"33/100"ใน.33กรณีนี้
dimo414

ด้วยคุณสมบัติเฉพาะของภาษาใดก็ได้ ไม่ชัดเจนว่าคุณกำลังถามอะไรหากไม่ใช่แค่ความขัดแย้งในแง่
Marquis of Lorne

คำตอบ:


70

ฉันพบว่าการหาค่าประมาณอย่างมีเหตุผลของ David Eppstein เพื่อให้รหัส C จำนวนจริงตรงกับสิ่งที่คุณต้องการ มันขึ้นอยู่กับทฤษฎีของเศษส่วนอย่างต่อเนื่องและรวดเร็วและค่อนข้างกะทัดรัด

ฉันได้ใช้เวอร์ชันที่กำหนดเองนี้สำหรับขีด จำกัด ตัวเศษและตัวส่วนโดยเฉพาะ

/*
** find rational approximation to given real number
** David Eppstein / UC Irvine / 8 Aug 1993
**
** With corrections from Arno Formella, May 2008
**
** usage: a.out r d
**   r is real number to approx
**   d is the maximum denominator allowed
**
** based on the theory of continued fractions
** if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
** then best approximation is found by truncating this series
** (with some adjustments in the last term).
**
** Note the fraction can be recovered as the first column of the matrix
**  ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
**  ( 1  0 ) ( 1  0 ) ( 1  0 )
** Instead of keeping the sequence of continued fraction terms,
** we just keep the last partial product of these matrices.
*/

#include <stdio.h>

main(ac, av)
int ac;
char ** av;
{
    double atof();
    int atoi();
    void exit();

    long m[2][2];
    double x, startx;
    long maxden;
    long ai;

    /* read command line arguments */
    if (ac != 3) {
        fprintf(stderr, "usage: %s r d\n",av[0]);  // AF: argument missing
        exit(1);
    }
    startx = x = atof(av[1]);
    maxden = atoi(av[2]);

    /* initialize matrix */
    m[0][0] = m[1][1] = 1;
    m[0][1] = m[1][0] = 0;

    /* loop finding terms until denom gets too big */
    while (m[1][0] *  ( ai = (long)x ) + m[1][1] <= maxden) {
        long t;
        t = m[0][0] * ai + m[0][1];
        m[0][1] = m[0][0];
        m[0][0] = t;
        t = m[1][0] * ai + m[1][1];
        m[1][1] = m[1][0];
        m[1][0] = t;
        if(x==(double)ai) break;     // AF: division by zero
        x = 1/(x - (double) ai);
        if(x>(double)0x7FFFFFFF) break;  // AF: representation failure
    } 

    /* now remaining x is between 0 and 1/ai */
    /* approx as either 0 or 1/m where m is max that will fit in maxden */
    /* first try zero */
    printf("%ld/%ld, error = %e\n", m[0][0], m[1][0],
           startx - ((double) m[0][0] / (double) m[1][0]));

    /* now try other possibility */
    ai = (maxden - m[1][1]) / m[1][0];
    m[0][0] = m[0][0] * ai + m[0][1];
    m[1][0] = m[1][0] * ai + m[1][1];
    printf("%ld/%ld, error = %e\n", m[0][0], m[1][0],
           startx - ((double) m[0][0] / (double) m[1][0]));
}

6
สำหรับผู้ที่กำลังมองหาวิธีแก้ปัญหาใน Ruby เราโชคดี! คริสโตเฟอร์ลอร์ดได้ใช้อัลกอริทึมข้างต้นในอัญมณีทับทิม ดูchristopher.lord.ac/fractions-in-rubyและrubygems.org/gems/fraction
ฉาย

6
โปรดทราบว่ามีบางกรณีขอบที่รหัสนี้ไม่สามารถจัดการได้ดี: เมื่อให้ -1.3333333 โดยมีตัวส่วนสูงสุดเป็น 4 จะส่งกลับ 4 / -3 โดยมีข้อผิดพลาด 3.333333e-08 และ -5/4 โดยมีข้อผิดพลาด = -8.333330e-02 ซึ่งถูกต้อง แต่เมื่อให้ -1.33333337 ด้วยตัวหารสูงสุดเดียวกันจะเปลี่ยนเป็น 12121211 / -9090908 โดยมีข้อผิดพลาด error = 4.218847e-15 และ -4/3 โดยมีข้อผิดพลาด -3.666667e-08 ซึ่งไม่ถูกต้อง นี่เป็นปัญหาโดยเฉพาะอย่างยิ่งเมื่อนำเสนออัลกอริทึมที่มีตัวเลขทศนิยมที่คำนวณได้เช่น -4/3 ซึ่งให้ผลลัพธ์ที่ไม่ถูกต้องเช่นนี้
edsko

27

จาก Python 2.6 มีfractionsโมดูล

(อ้างจากเอกสาร)

>>> from fractions import Fraction
>>> Fraction('3.1415926535897932').limit_denominator(1000)
Fraction(355, 113)

>>> from math import pi, cos
>>> Fraction.from_float(cos(pi/3))
Fraction(4503599627370497, 9007199254740992)
>>> Fraction.from_float(cos(pi/3)).limit_denominator()
Fraction(1, 2)

6
บันทึกการใช้งานและอัลกอริทึมที่hg.python.org/cpython/file/822c7c0d27d1/Lib/fractions.py#l211
piro

2
@Debilski คำตอบของ OP language agnosticและalgorithmแท็กใดที่ตอบสนองความต้องการของคุณ?
vladr

2
@vladr ดีเนื่องจากฉันเขียนคำตอบนี้เมื่อเกือบ 6 ปีที่แล้ว (และมากกว่าหนึ่งปีหลังจากคำถามถูกถาม) ฉันเดาว่าฉันไม่รู้อีกต่อไปว่าเหตุผลของฉันในตอนนั้นคืออะไร ส่วนใหญ่ฉันอาจอ้างถึงความคิดเห็นนี้: stackoverflow.com/questions/95727/… OTOH อาจเป็นไปได้ว่าคำตอบนี้รวมเข้ากับคำถามอื่น ใครสามารถบอกได้หลังจากผ่านไปหลายปี…
เดบิลสกี

คุณสามารถเพิ่มประโยคสองสามประโยคเกี่ยวกับอัลกอริทึมที่ใช้โดยโมดูลเศษส่วน (และอาจอัปเดตคำตอบของคุณสำหรับ Python3)
einpoklum

21

หากผลลัพธ์คือการให้ผู้อ่านที่เป็นมนุษย์ประทับใจในลำดับของผลลัพธ์อย่างรวดเร็วมันก็ไม่มีเหตุผลที่จะคืนค่าบางอย่างเช่น "113/211" ดังนั้นผลลัพธ์ควร จำกัด ตัวเองให้ใช้ตัวเลขหนึ่งหลัก (และอาจเป็น 1 / 10 และ 9/10) ถ้าเป็นเช่นนั้นคุณสามารถสังเกตเห็นว่ามีเพียง 27 ที่แตกต่างกันเศษส่วน

เนื่องจากคณิตศาสตร์พื้นฐานในการสร้างผลลัพธ์จะไม่มีการเปลี่ยนแปลงวิธีแก้ปัญหาอาจเป็นเพียงการฮาร์ดโค้ดโครงสร้างการค้นหาแบบไบนารีเพื่อให้ฟังก์ชันทำงานได้สูงสุด (27) ~ = 4 3/4 การเปรียบเทียบ นี่คือรหัสรุ่น C ที่ผ่านการทดสอบ

char *userTextForDouble(double d, char *rval)
{
    if (d == 0.0)
        return "0";

    // TODO: negative numbers:if (d < 0.0)...
    if (d >= 1.0)
        sprintf(rval, "%.0f ", floor(d));
    d = d-floor(d); // now only the fractional part is left

    if (d == 0.0)
        return rval;

    if( d < 0.47 )
    {
        if( d < 0.25 )
        {
            if( d < 0.16 )
            {
                if( d < 0.12 ) // Note: fixed from .13
                {
                    if( d < 0.11 )
                        strcat(rval, "1/10"); // .1
                    else
                        strcat(rval, "1/9"); // .1111....
                }
                else // d >= .12
                {
                    if( d < 0.14 )
                        strcat(rval, "1/8"); // .125
                    else
                        strcat(rval, "1/7"); // .1428...
                }
            }
            else // d >= .16
            {
                if( d < 0.19 )
                {
                    strcat(rval, "1/6"); // .1666...
                }
                else // d > .19
                {
                    if( d < 0.22 )
                        strcat(rval, "1/5"); // .2
                    else
                        strcat(rval, "2/9"); // .2222...
                }
            }
        }
        else // d >= .25
        {
            if( d < 0.37 ) // Note: fixed from .38
            {
                if( d < 0.28 ) // Note: fixed from .29
                {
                    strcat(rval, "1/4"); // .25
                }
                else // d >=.28
                {
                    if( d < 0.31 )
                        strcat(rval, "2/7"); // .2857...
                    else
                        strcat(rval, "1/3"); // .3333...
                }
            }
            else // d >= .37
            {
                if( d < 0.42 ) // Note: fixed from .43
                {
                    if( d < 0.40 )
                        strcat(rval, "3/8"); // .375
                    else
                        strcat(rval, "2/5"); // .4
                }
                else // d >= .42
                {
                    if( d < 0.44 )
                        strcat(rval, "3/7"); // .4285...
                    else
                        strcat(rval, "4/9"); // .4444...
                }
            }
        }
    }
    else
    {
        if( d < 0.71 )
        {
            if( d < 0.60 )
            {
                if( d < 0.55 ) // Note: fixed from .56
                {
                    strcat(rval, "1/2"); // .5
                }
                else // d >= .55
                {
                    if( d < 0.57 )
                        strcat(rval, "5/9"); // .5555...
                    else
                        strcat(rval, "4/7"); // .5714
                }
            }
            else // d >= .6
            {
                if( d < 0.62 ) // Note: Fixed from .63
                {
                    strcat(rval, "3/5"); // .6
                }
                else // d >= .62
                {
                    if( d < 0.66 )
                        strcat(rval, "5/8"); // .625
                    else
                        strcat(rval, "2/3"); // .6666...
                }
            }
        }
        else
        {
            if( d < 0.80 )
            {
                if( d < 0.74 )
                {
                    strcat(rval, "5/7"); // .7142...
                }
                else // d >= .74
                {
                    if(d < 0.77 ) // Note: fixed from .78
                        strcat(rval, "3/4"); // .75
                    else
                        strcat(rval, "7/9"); // .7777...
                }
            }
            else // d >= .8
            {
                if( d < 0.85 ) // Note: fixed from .86
                {
                    if( d < 0.83 )
                        strcat(rval, "4/5"); // .8
                    else
                        strcat(rval, "5/6"); // .8333...
                }
                else // d >= .85
                {
                    if( d < 0.87 ) // Note: fixed from .88
                    {
                        strcat(rval, "6/7"); // .8571
                    }
                    else // d >= .87
                    {
                        if( d < 0.88 ) // Note: fixed from .89
                        {
                            strcat(rval, "7/8"); // .875
                        }
                        else // d >= .88
                        {
                            if( d < 0.90 )
                                strcat(rval, "8/9"); // .8888...
                            else
                                strcat(rval, "9/10"); // .9
                        }
                    }
                }
            }
        }
    }

    return rval;
}

3
นี่คือความคิดแนวข้างที่เราต้องการมากกว่านี้! ข้อเสนอแนะที่ยอดเยี่ยม
edsko

1
เป็นวิธีที่น่าเกลียดเล็กน้อย แต่รวดเร็วและใช้งานได้จริง
Bosak

1
นี่เป็นแนวทางที่น่าสนใจที่เรียบง่ายอย่างน่าอัศจรรย์ หากต้องการประหยัดพื้นที่คุณสามารถค้นหาอาร์เรย์แบบไบนารีแทนหรือสร้างต้นไม้ไบนารีได้ แต่วิธีการของคุณอาจเร็วกว่าเล็กน้อย (คุณสามารถประหยัดพื้นที่ได้โดยใช้การเรียก strcat ครั้งเดียวก่อนที่จะส่งคืนและกำหนด var ที่เรียกว่าตอนนี้) นอกจากนี้ฉันจะรวม 3/10 และ 7/10 แต่นั่นอาจเป็นแค่ฉัน
jimhark

1
ด้วยแรงบันดาลใจจากโซลูชันนี้ฉันได้สร้างโค้ดสั้น ๆ (แต่ไม่ได้เพิ่มประสิทธิภาพทั้งหมด) สามารถขยายให้ครอบคลุมเศษส่วนจำนวนมากได้อย่างง่ายดาย jsfiddle.net/PdL23/1
Deepak Joy

1
โปรดทราบว่า1/1000ยังสามารถอ่านได้โดยมนุษย์ แต่อัลกอริทึมข้างต้นจะสร้างการ1/10ประมาณที่หยาบมากเท่านั้น ผมเชื่อว่าการปรับปรุงที่สามารถทำในแง่ของการที่ตัวหารที่สามารถอ่านได้อย่างมนุษย์ปุถุชนคนหนึ่งสามารถเลือกจากและ / หรือการเพิ่มขึ้นของ<, >, <<, >>คำนำหน้าเพื่อให้ความคิดของความเลวของประมาณที่
vladr

16

นี่คือลิงค์ที่อธิบายคณิตศาสตร์เบื้องหลังการแปลงทศนิยมเป็นเศษส่วน:

http://www.webmath.com/dec2fract.html

และนี่คือฟังก์ชั่นตัวอย่างสำหรับการใช้งานจริงโดยใช้ VB (จาก www.freevbcode.com/ShowCode.asp?ID=582):

Public Function Dec2Frac(ByVal f As Double) As String

   Dim df As Double
   Dim lUpperPart As Long
   Dim lLowerPart As Long

   lUpperPart = 1
   lLowerPart = 1

   df = lUpperPart / lLowerPart
   While (df <> f)
      If (df < f) Then
         lUpperPart = lUpperPart + 1
      Else
         lLowerPart = lLowerPart + 1
         lUpperPart = f * lLowerPart
      End If
      df = lUpperPart / lLowerPart
   Wend
Dec2Frac = CStr(lUpperPart) & "/" & CStr(lLowerPart)
End Function

(จากการค้นหาของ Google: แปลงทศนิยมเป็นเศษส่วน, แปลงทศนิยมเป็นรหัสเศษส่วน)


2
โปรดทราบว่าอัลกอริทึมนี้ใช้เวลาΩ (m) เมื่อ f = n / m และอาจเป็นได้มากแม้ว่าคุณจะไม่ได้ตั้งใจก็ตาม (พิจารณา 0.66666666667)
einpoklum

10

คุณอาจต้องการที่จะอ่านอะไรทุกนักวิทยาศาสตร์คอมพิวเตอร์ควรรู้เกี่ยวกับจุดลอยเลขคณิต

คุณจะต้องระบุความแม่นยำโดยการคูณด้วยจำนวนมาก:

3.141592 * 1000000 = 3141592

จากนั้นคุณสามารถสร้างเศษส่วน:

3 + (141592 / 1000000)

และลดผ่าน GCD ...

3 + (17699 / 125000)

แต่ไม่มีทางเอาเศษส่วนที่ตั้งใจไว้ออกมาได้ คุณอาจต้องการที่จะเสมอใช้เศษส่วนทั่วรหัสของคุณแทน --just จำเพื่อลดเศษส่วนเมื่อคุณสามารถที่จะหลีกเลี่ยงล้น!


9

นี่คือโค้ด VB เวอร์ชัน Perl และ Javascript ที่ devinmoore แนะนำ:

Perl:

sub dec2frac {
    my $d = shift;

    my $df  = 1;
    my $top = 1;
    my $bot = 1;

    while ($df != $d) {
      if ($df < $d) {
        $top += 1;
      }
      else {
         $bot += 1;
         $top = int($d * $bot);
      }
      $df = $top / $bot;
   }
   return "$top/$bot";
}

และจาวาสคริปต์ที่เหมือนกันเกือบทั้งหมด:

function dec2frac(d) {

    var df = 1;
    var top = 1;
    var bot = 1;

    while (df != d) {
        if (df < d) {
            top += 1;
        }
        else {
            bot += 1;
            top = parseInt(d * bot);
        }
        df = top / bot;
    }
    return top + '/' + bot;
}

9

การใช้งาน AC #

/// <summary>
/// Represents a rational number
/// </summary>
public struct Fraction
{
    public int Numerator;
    public int Denominator;

    /// <summary>
    /// Constructor
    /// </summary>
    public Fraction(int numerator, int denominator)
    {
        this.Numerator = numerator;
        this.Denominator = denominator;
    }

    /// <summary>
    /// Approximates a fraction from the provided double
    /// </summary>
    public static Fraction Parse(double d)
    {
        return ApproximateFraction(d);
    }

    /// <summary>
    /// Returns this fraction expressed as a double, rounded to the specified number of decimal places.
    /// Returns double.NaN if denominator is zero
    /// </summary>
    public double ToDouble(int decimalPlaces)
    {
        if (this.Denominator == 0)
            return double.NaN;

        return System.Math.Round(
            Numerator / (double)Denominator,
            decimalPlaces
        );
    }


    /// <summary>
    /// Approximates the provided value to a fraction.
    /// http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions
    /// </summary>
    private static Fraction ApproximateFraction(double value)
    {
        const double EPSILON = .000001d;

        int n = 1;  // numerator
        int d = 1;  // denominator
        double fraction = n / d;

        while (System.Math.Abs(fraction - value) > EPSILON)
        {
            if (fraction < value)
            {
                n++;
            }
            else
            {
                d++;
                n = (int)System.Math.Round(value * d);
            }

            fraction = n / (double)d;
        }

        return new Fraction(n, d);
    }
}


6

ปัญหาส่วนหนึ่งคือเศษส่วนจำนวนมากไม่ได้ถูกตีความว่าเป็นเศษส่วนอย่างง่ายดาย เช่น 0.33 ไม่ใช่ 1/3 มันคือ 33/100 แต่ถ้าคุณจำการฝึกในโรงเรียนประถมได้มีกระบวนการแปลงค่าทศนิยมเป็นเศษส่วนอย่างไรก็ตามไม่น่าจะให้สิ่งที่คุณต้องการเนื่องจากตัวเลขทศนิยมส่วนใหญ่ไม่ได้เก็บไว้ที่ 0.33 แต่ 0.329999999999998 หรือบางส่วน

ทำตัวเป็นที่โปรดปรานและไม่ต้องกังวลกับสิ่งนี้ แต่ถ้าคุณต้องการคุณสามารถทำสิ่งต่อไปนี้:

คูณค่าเดิมด้วย 10 จนกว่าคุณจะเอาส่วนที่เป็นเศษส่วนออก เก็บตัวเลขนั้นไว้และใช้เป็นตัวหาร จากนั้นทำชุดของการทำให้เข้าใจง่ายโดยมองหาตัวส่วนร่วม

0.4 จึงเท่ากับ 4/10 จากนั้นคุณจะมองหาตัวหารทั่วไปที่เริ่มต้นด้วยค่าต่ำซึ่งอาจเป็นจำนวนเฉพาะ เริ่มต้นด้วย 2 คุณจะเห็นว่า 2 หารทั้งตัวเศษและตัวส่วนเท่า ๆ กันหรือไม่โดยตรวจสอบว่าพื้นของการหารเหมือนกับการหารหรือไม่

floor(5/2) = 2
5/2 = 2.5

ดังนั้น 5 ไม่หาร 2 เท่า ๆ กัน จากนั้นคุณตรวจสอบหมายเลขถัดไปพูดว่า 3 คุณทำเช่นนี้จนกว่าคุณจะตีหรือเหนือรากที่สองของจำนวนที่น้อยกว่า

หลังจากที่คุณทำแล้วคุณต้อง


1
ฉันขอแนะนำให้ใช้อัลกอริทึมแบบยุคลิดสำหรับขั้นตอนสุดท้าย
Graphics Noob


4

"สมมติว่าเรามี 0.33 เราต้องส่งออก" 1/3 "

คุณคาดหวังให้ "โซลูชัน" มีความแม่นยำเพียงใด 0.33 ไม่เท่ากับ 1/3 คุณรู้จักคำตอบที่ "ดี" (อ่านง่าย) ได้อย่างไร?

ไม่ว่าจะเป็นอย่างไรก็ตามอัลกอริทึมที่เป็นไปได้อาจเป็น:

หากคุณคาดว่าจะพบเศษส่วนที่ใกล้ที่สุดในรูปแบบ X / Y โดยที่ Y น้อยกว่า 10 คุณสามารถวนซ้ำ Ys ที่เป็นไปได้ทั้งหมด 9 ตัวสำหรับ Y แต่ละการคำนวณ X จากนั้นเลือกค่าที่แม่นยำที่สุด


3

ฉันคิดว่าวิธีที่ดีที่สุดในการทำสิ่งนี้คือการแปลงค่า float ของคุณเป็นการแสดง ascii ก่อน ใน C ++ คุณสามารถใช้ ostringstream หรือในภาษา C คุณสามารถใช้ sprintf นี่คือลักษณะที่ปรากฏใน C ++:

ostringstream oss;
float num;
cin >> num;
oss << num;
string numStr = oss.str();
int i = numStr.length(), pow_ten = 0;
while (i > 0) {
    if (numStr[i] == '.')
        break;
    pow_ten++;
    i--;
}
for (int j = 1; j < pow_ten; j++) {
    num *= 10.0;
}
cout << static_cast<int>(num) << "/" << pow(10, pow_ten - 1) << endl;

แนวทางที่คล้ายกันนี้สามารถนำมาใช้ในแนวตรง C

หลังจากนั้นคุณจะต้องตรวจสอบว่าเศษส่วนอยู่ในเงื่อนไขต่ำสุด อัลกอริทึมนี้จะให้คำตอบที่แม่นยำกล่าวคือ 0.33 จะแสดงผลเป็น "33/100" ไม่ใช่ "1/3" อย่างไรก็ตาม 0.4 จะให้ "4/10" ซึ่งเมื่อลดเป็นเงื่อนไขต่ำสุดจะเป็น "2/5" สิ่งนี้อาจไม่ทรงพลังเท่าโซลูชันของ EppStein แต่ฉันเชื่อว่านี่ตรงไปตรงมามากกว่า


8 ปีต่อมาฉันได้พบกับโซลูชันของคุณฉันได้ทดสอบและทำงานได้อย่างสมบูรณ์แบบแล้ว แต่คุณบอกว่ามันไม่ได้มีประสิทธิภาพเท่ากับโซลูชันของ EppStein และฉันสงสัยว่าทำไม เนื่องจากวิธีการแก้ปัญหาของคุณนั้นง่ายกว่ามากจึงไม่ควรเป็นทางเลือกของทางเลือกเราจึงไม่ได้ตั้งใจที่จะทำโค้ดที่ง่ายที่สุดเท่าที่จะทำได้ตราบเท่าที่มันใช้งานได้และปลอดภัยหรือไม่?
HBatalha

3

โซลูชันในตัวใน R:

library(MASS)
fractions(0.666666666)
## [1] 2/3

สิ่งนี้ใช้วิธีเศษส่วนอย่างต่อเนื่องและมีทางเลือกcyclesและmax.denominatorอาร์กิวเมนต์สำหรับปรับความแม่นยำ


นอกจากนี้library(numbers)และcontFrac(0.6666); เพื่อให้ได้เอาต์พุตสตริงตามที่ต้องการ:paste(contFrac(0.666, tol=1e-03)$rat, collapse="/")
rbatt

2

คุณจะต้องพิจารณาว่าคุณยินดียอมรับข้อผิดพลาดระดับใด ไม่ใช่เศษส่วนทศนิยมทั้งหมดที่จะลดเป็นเศษส่วนอย่างง่าย ฉันอาจจะเลือกจำนวนที่หารได้ง่ายเช่น 60 แล้วหาจำนวน 60 ที่ใกล้เคียงกับค่ามากที่สุดจากนั้นจึงทำให้เศษส่วนง่ายขึ้น


2

คุณสามารถทำได้ในภาษาโปรแกรมใด ๆ โดยใช้ขั้นตอนต่อไปนี้:

  1. คูณและหารด้วย 10 ^ x โดยที่ x คือกำลังของ 10 ที่ต้องการเพื่อให้แน่ใจว่าตัวเลขนั้นไม่มีตำแหน่งทศนิยมเหลืออยู่ ตัวอย่าง: คูณ 0.33 ด้วย 10 ^ 2 = 100 เพื่อให้ได้ 33 และหารด้วยสิ่งเดียวกันเพื่อให้ได้ 33/100
  2. ลดตัวเศษและตัวส่วนของเศษที่เป็นผลลัพธ์โดยการแยกตัวประกอบจนกว่าคุณจะไม่สามารถหาจำนวนเต็มจากผลลัพธ์ได้อีกต่อไป
  3. เศษส่วนที่ลดลงควรเป็นคำตอบของคุณ

ตัวอย่าง: 0.2 = 0.2 x 10 ^ 1/10 ^ 1 = 2/10 = 1/5

ดังนั้นจึงสามารถอ่านได้ว่า '1 ส่วนจาก 5'


2

ทางออกหนึ่งคือเก็บตัวเลขทั้งหมดไว้เป็นตัวเลขที่มีเหตุผลตั้งแต่แรก มีไลบรารีสำหรับเลขคณิตจำนวนตรรกยะ (เช่นGMP ) หากใช้ภาษา OO คุณอาจสามารถใช้ไลบรารีคลาสจำนวนที่มีเหตุผลเพื่อแทนที่คลาสตัวเลขของคุณได้

โปรแกรมการเงินอื่น ๆ จะใช้วิธีแก้ปัญหาดังกล่าวเพื่อให้สามารถคำนวณได้อย่างถูกต้องและรักษาความแม่นยำที่อาจสูญเสียไปโดยใช้ลูกลอยธรรมดา

แน่นอนว่ามันจะช้าลงมากดังนั้นจึงอาจไม่เป็นประโยชน์สำหรับคุณ ขึ้นอยู่กับการคำนวณที่คุณต้องทำและความแม่นยำนั้นสำคัญสำหรับคุณเพียงใด

a = rational(1);
b = rational(3);
c = a / b;

print (c.asFraction)  --->  "1/3"
print (c.asFloat) ----> "0.333333"

2

สมมติว่าเรามี 0.33 เราต้องเอาท์พุท "1/3" ถ้าเรามี "0.4" เราต้องส่งออก "2/5"

มันผิดในกรณีทั่วไปเนื่องจาก 1/3 = 0.3333333 = 0 (3) ยิ่งไปกว่านั้นมันเป็นไปไม่ได้ที่จะหาคำตอบจากคำแนะนำข้างต้นคือทศนิยมสามารถแปลงเป็นเศษส่วนด้วยความแม่นยำที่กำหนดได้เนื่องจากผลลัพธ์เป็นเศษส่วนเสมอ

แต่ฉันขอแนะนำฟังก์ชันที่ครอบคลุมของฉันด้วยตัวเลือกมากมายตามแนวคิดของอนุกรมเรขาคณิตไม่มีที่สิ้นสุดโดยเฉพาะในสูตร:

ป้อนคำอธิบายภาพที่นี่

ในตอนแรกฟังก์ชันนี้พยายามหาช่วงเวลาเศษส่วนในการแทนค่าสตริง หลังจากนั้นจะใช้สูตรที่อธิบายไว้ข้างต้น

รหัสตัวเลขที่มีเหตุผลยืมมาจากการใช้ตัวเลขเชิงเหตุผลของStephen M. McKameyใน C # ฉันหวังว่าจะไม่ยากที่จะพอร์ตรหัสของฉันเป็นภาษาอื่น

/// <summary>
/// Convert decimal to fraction
/// </summary>
/// <param name="value">decimal value to convert</param>
/// <param name="result">result fraction if conversation is succsess</param>
/// <param name="decimalPlaces">precision of considereation frac part of value</param>
/// <param name="trimZeroes">trim zeroes on the right part of the value or not</param>
/// <param name="minPeriodRepeat">minimum period repeating</param>
/// <param name="digitsForReal">precision for determination value to real if period has not been founded</param>
/// <returns></returns>
public static bool FromDecimal(decimal value, out Rational<T> result, 
    int decimalPlaces = 28, bool trimZeroes = false, decimal minPeriodRepeat = 2, int digitsForReal = 9)
{
    var valueStr = value.ToString("0.0000000000000000000000000000", CultureInfo.InvariantCulture);
    var strs = valueStr.Split('.');

    long intPart = long.Parse(strs[0]);
    string fracPartTrimEnd = strs[1].TrimEnd(new char[] { '0' });
    string fracPart;

    if (trimZeroes)
    {
        fracPart = fracPartTrimEnd;
        decimalPlaces = Math.Min(decimalPlaces, fracPart.Length);
    }
    else
        fracPart = strs[1];

    result = new Rational<T>();
    try
    {
        string periodPart;
        bool periodFound = false;

        int i;
        for (i = 0; i < fracPart.Length; i++)
        {
            if (fracPart[i] == '0' && i != 0)
                continue;

            for (int j = i + 1; j < fracPart.Length; j++)
            {
                periodPart = fracPart.Substring(i, j - i);
                periodFound = true;
                decimal periodRepeat = 1;
                decimal periodStep = 1.0m / periodPart.Length;
                var upperBound = Math.Min(fracPart.Length, decimalPlaces);
                int k;
                for (k = i + periodPart.Length; k < upperBound; k += 1)
                {
                    if (periodPart[(k - i) % periodPart.Length] != fracPart[k])
                    {
                        periodFound = false;
                        break;
                    }
                    periodRepeat += periodStep;
                }

                if (!periodFound && upperBound - k <= periodPart.Length && periodPart[(upperBound - i) % periodPart.Length] > '5')
                {
                    var ind = (k - i) % periodPart.Length;
                    var regroupedPeriod = (periodPart.Substring(ind) + periodPart.Remove(ind)).Substring(0, upperBound - k);
                    ulong periodTailPlusOne = ulong.Parse(regroupedPeriod) + 1;
                    ulong fracTail = ulong.Parse(fracPart.Substring(k, regroupedPeriod.Length));
                    if (periodTailPlusOne == fracTail)
                        periodFound = true;
                }

                if (periodFound && periodRepeat >= minPeriodRepeat)
                {
                    result = FromDecimal(strs[0], fracPart.Substring(0, i), periodPart);
                    break;
                }
                else
                    periodFound = false;
            }

            if (periodFound)
                break;
        }

        if (!periodFound)
        {
            if (fracPartTrimEnd.Length >= digitsForReal)
                return false;
            else
            {
                result = new Rational<T>(long.Parse(strs[0]), 1, false);
                if (fracPartTrimEnd.Length != 0)
                    result = new Rational<T>(ulong.Parse(fracPartTrimEnd), TenInPower(fracPartTrimEnd.Length));
                return true;
            }
        }

        return true;
    }
    catch
    {
        return false;
    }
}

public static Rational<T> FromDecimal(string intPart, string fracPart, string periodPart)
{
    Rational<T> firstFracPart;
    if (fracPart != null && fracPart.Length != 0)
    {
        ulong denominator = TenInPower(fracPart.Length);
        firstFracPart = new Rational<T>(ulong.Parse(fracPart), denominator);
    }
    else
        firstFracPart = new Rational<T>(0, 1, false);

    Rational<T> secondFracPart;
    if (periodPart != null && periodPart.Length != 0)
        secondFracPart =
            new Rational<T>(ulong.Parse(periodPart), TenInPower(fracPart.Length)) *
            new Rational<T>(1, Nines((ulong)periodPart.Length), false);
    else
        secondFracPart = new Rational<T>(0, 1, false);

    var result = firstFracPart + secondFracPart;
    if (intPart != null && intPart.Length != 0)
    {
        long intPartLong = long.Parse(intPart);
        result = new Rational<T>(intPartLong, 1, false) + (intPartLong == 0 ? 1 : Math.Sign(intPartLong)) * result;
    }

    return result;
}

private static ulong TenInPower(int power)
{
    ulong result = 1;
    for (int l = 0; l < power; l++)
        result *= 10;
    return result;
}

private static decimal TenInNegPower(int power)
{
    decimal result = 1;
    for (int l = 0; l > power; l--)
        result /= 10.0m;
    return result;
}

private static ulong Nines(ulong power)
{
    ulong result = 9;
    if (power >= 0)
        for (ulong l = 0; l < power - 1; l++)
            result = result * 10 + 9;
    return result;
}

มีตัวอย่างการใช้งานบางส่วน:

Rational<long>.FromDecimal(0.33333333m, out r, 8, false);
// then r == 1 / 3;

Rational<long>.FromDecimal(0.33333333m, out r, 9, false);
// then r == 33333333 / 100000000;

กรณีของคุณที่มีการตัดส่วนด้านขวาเป็นศูนย์:

Rational<long>.FromDecimal(0.33m, out r, 28, true);
// then r == 1 / 3;

Rational<long>.FromDecimal(0.33m, out r, 28, true);
// then r == 33 / 100;

การกำจัดระยะเวลาขั้นต่ำ:

Rational<long>.FromDecimal(0.123412m, out r, 28, true, 1.5m));
// then r == 1234 / 9999;
Rational<long>.FromDecimal(0.123412m, out r, 28, true, 1.6m));
// then r == 123412 / 1000000; because of minimu repeating of period is 0.1234123 in this case.

การปัดเศษในตอนท้าย:

Rational<long>.FromDecimal(0.8888888888888888888888888889m, out r));
// then r == 8 == 9;

กรณีที่น่าสนใจที่สุด:

Rational<long>.FromDecimal(0.12345678m, out r, 28, true, 2, 9);
// then r == 12345678 / 100000000;

Rational<long>.FromDecimal(0.12345678m, out r, 28, true, 2, 8);
// Conversation failed, because of period has not been founded and there are too many digits in fraction part of input value.

Rational<long>.FromDecimal(0.12121212121212121m, out r, 28, true, 2, 9));
// then r == 4 / 33; Despite of too many digits in input value, period has been founded. Thus it's possible to convert value to fraction.

การทดสอบอื่น ๆ และรหัสทุกคนสามารถหาในMathFunctions ของฉันห้องสมุดบน GitHub


2

Ruby มีโซลูชันในตัวอยู่แล้ว:

0.33.rationalize.to_s # => "33/100"
0.4.rationalize.to_s # => "2/5"

ใน Rails สามารถแปลงแอตทริบิวต์ตัวเลข ActiveRecord ได้เช่นกัน:

product.size = 0.33
product.size.to_r.to_s # => "33/100"

2

คำตอบในภาษา C ++ สมมติว่าคุณมีคลาส 'BigInt' ซึ่งสามารถจัดเก็บจำนวนเต็มไม่ จำกัด ขนาดได้

คุณสามารถใช้ 'unsigned long long' แทนได้ แต่จะใช้ได้กับค่าบางค่าเท่านั้น

void GetRational(double val)
{
    if (val == val+1) // Inf
        throw "Infinite Value";
    if (val != val) // NaN
        throw "Undefined Value";

    bool sign = false;
    BigInt enumerator = 0;
    BigInt denominator = 1;

    if (val < 0)
    {
        val = -val;
        sign = true;
    }

    while (val > 0)
    {
        unsigned int intVal = (unsigned int)val;
        val -= intVal;
        enumerator += intVal;
        val *= 2;
        enumerator *= 2;
        denominator *= 2;
    }

    BigInt gcd = GCD(enumerator,denominator);
    enumerator /= gcd;
    denominator /= gcd;

    Print(sign? "-":"+");
    Print(enumerator);
    Print("/");
    Print(denominator);

    // Or simply return {sign,enumerator,denominator} as you wish
}

BTW, GetRational (0.0) จะส่งคืน "+0/1" ดังนั้นคุณอาจต้องการจัดการกรณีนี้แยกกัน

PS: ฉันใช้รหัสนี้ในคลาส 'RationalNum' ของตัวเองมาหลายปีแล้วและได้รับการทดสอบอย่างละเอียด


ตัวอย่างของคุณดูเหมือนจะแจกแจงค่าต่างๆเช่น 1.333333 .. มันวนไปวนมาที่ยาวมากพยายามหาค่าและดูเหมือนจะไม่ได้ผล ... ใช้ได้ดีกับค่าง่ายๆอื่น ๆ เช่น 1.25
Adamski

@Adamski: ขอบคุณ ช่วงเวลา "คอนเวอร์เจนซ์" ของwhileลูปถูกล้อมรอบด้วยขนาดdoubleซึ่งโดยทั่วไปคือ 64 บิต ดังนั้นจึงไม่ขึ้นอยู่กับค่าเริ่มต้นของอินพุต ( val) อย่างไรก็ตามGCDฟังก์ชันจะขึ้นอยู่กับค่านี้แม้ว่าโดยปกติแล้วจะรวมเข้ากับโซลูชันได้ค่อนข้างเร็ว เป็นไปได้ไหมว่าคุณใช้ฟังก์ชันนี้ไม่ถูกต้อง
barak manos

@Adamski: นอกจากนี้ตามที่ฉันได้กล่าวไว้ในตอนต้นของคำตอบหากคุณใช้unsigned long longแทนBigIntมันก็ไม่จำเป็นต้องให้ผลลัพธ์ที่ถูกต้องสำหรับทุกค่าอินพุต ... แต่ถึงแม้จะอยู่ภายใต้สถานการณ์นั้นรหัสก็ไม่ได้ ควรจะ "วนรอบที่ยาวมาก"
barak manos

โอเคใช่นั่นเป็นไปได้ทั้งหมดฟังก์ชัน GCD ที่ฉันใช้เป็นส่วนหนึ่งของคลาส BigInteger ของไลบรารี Juce ขอบคุณสำหรับข้อมูล!
Adamski

@Adamski: ดังนั้นจึงไม่สมเหตุสมผลที่จะใช้GCDฟังก์ชันไม่ถูกต้อง คุณตรวจสอบว่าโค้ดทำงานเป็นเวลานานระหว่างwhileลูปหรือหลังจากนั้นหรือไม่? ฉันจะตรวจสอบค่า 1.33333 เพื่อดูว่ามีอะไรอยู่เบื้องหลังสิ่งนี้ ขอบคุณ.
barak manos

2

อัลกอริทึมนี้โดยIan Richards / John Kennedyไม่เพียง แต่ส่งกลับเศษส่วนที่ดีเท่านั้น แต่ยังทำงานได้ดีในแง่ของความเร็วอีกด้วย นี่คือรหัส C # ที่นำมาจากคำตอบนี้โดยฉัน

สามารถจัดการdoubleค่าทั้งหมดยกเว้นค่าพิเศษเช่น NaN และ +/- infinity ซึ่งคุณจะต้องเพิ่มหากจำเป็น

มันส่งคืนไฟล์new Fraction(numerator, denominator). แทนที่ตามประเภทของคุณเอง

สำหรับค่าตัวอย่างเพิ่มเติมและการเปรียบเทียบกับอัลกอริทึมอื่น ๆไปที่นี่

public Fraction RealToFraction(double value, double accuracy)
{
    if (accuracy <= 0.0 || accuracy >= 1.0)
    {
        throw new ArgumentOutOfRangeException("accuracy", "Must be > 0 and < 1.");
    }

    int sign = Math.Sign(value);

    if (sign == -1)
    {
        value = Math.Abs(value);
    }

    // Accuracy is the maximum relative error; convert to absolute maxError
    double maxError = sign == 0 ? accuracy : value * accuracy;

    int n = (int) Math.Floor(value);
    value -= n;

    if (value < maxError)
    {
        return new Fraction(sign * n, 1);
    }

    if (1 - maxError < value)
    {
        return new Fraction(sign * (n + 1), 1);
    }

    double z = value;
    int previousDenominator = 0;
    int denominator = 1;
    int numerator;

    do
    {
        z = 1.0 / (z - (int) z);
        int temp = denominator;
        denominator = denominator * (int) z + previousDenominator;
        previousDenominator = temp;
        numerator = Convert.ToInt32(value * denominator);
    }
    while (Math.Abs(value - (double) numerator / denominator) > maxError && z != (int) z);

    return new Fraction((n * denominator + numerator) * sign, denominator);
}

ตัวอย่างค่าที่ส่งคืนโดยอัลกอริทึมนี้:

Accuracy: 1.0E-3      | Richards                     
Input                 | Result           Error       
======================| =============================
   3                  |       3/1          0         
   0.999999           |       1/1         1.0E-6     
   1.000001           |       1/1        -1.0E-6     
   0.50 (1/2)         |       1/2          0         
   0.33... (1/3)      |       1/3          0         
   0.67... (2/3)      |       2/3          0         
   0.25 (1/4)         |       1/4          0         
   0.11... (1/9)      |       1/9          0         
   0.09... (1/11)     |       1/11         0         
   0.62... (307/499)  |       8/13        2.5E-4     
   0.14... (33/229)   |      16/111       2.7E-4     
   0.05... (33/683)   |      10/207      -1.5E-4     
   0.18... (100/541)  |      17/92       -3.3E-4     
   0.06... (33/541)   |       5/82       -3.7E-4     
   0.1                |       1/10         0         
   0.2                |       1/5          0         
   0.3                |       3/10         0         
   0.4                |       2/5          0         
   0.5                |       1/2          0         
   0.6                |       3/5          0         
   0.7                |       7/10         0         
   0.8                |       4/5          0         
   0.9                |       9/10         0         
   0.01               |       1/100        0         
   0.001              |       1/1000       0         
   0.0001             |       1/10000      0         
   0.33333333333      |       1/3         1.0E-11    
   0.333              |     333/1000       0         
   0.7777             |       7/9         1.0E-4     
   0.11               |      10/91       -1.0E-3     
   0.1111             |       1/9         1.0E-4     
   3.14               |      22/7         9.1E-4     
   3.14... (pi)       |      22/7         4.0E-4     
   2.72... (e)        |      87/32        1.7E-4     
   0.7454545454545    |      38/51       -4.8E-4     
   0.01024801004      |       2/195       8.2E-4     
   0.99011            |     100/101      -1.1E-5     
   0.26... (5/19)     |       5/19         0         
   0.61... (37/61)    |      17/28        9.7E-4     
                      | 
Accuracy: 1.0E-4      | Richards                     
Input                 | Result           Error       
======================| =============================
   0.62... (307/499)  |     299/486      -6.7E-6     
   0.05... (33/683)   |      23/476       6.4E-5     
   0.06... (33/541)   |      33/541        0         
   1E-05              |       1/99999     1.0E-5     
   0.7777             |    1109/1426     -1.8E-7     
   3.14... (pi)       |     333/106      -2.6E-5     
   2.72... (e)        |     193/71        1.0E-5     
   0.61... (37/61)    |      37/61         0         

1

คุณจะมีปัญหาพื้นฐานสองประการที่จะทำให้ยาก:

1) จุดลอยตัวไม่ใช่การแสดงที่แน่นอนซึ่งหมายความว่าหากคุณมีเศษส่วนของ "x / y" ซึ่งส่งผลให้ค่าเป็น "z" อัลกอริทึมเศษส่วนของคุณอาจส่งคืนผลลัพธ์อื่นที่ไม่ใช่ "x / y"

2) มีจำนวนอตรรกยะมากกว่าจำนวนตรรกยะไม่มีที่สิ้นสุด จำนวนตรรกยะคือจำนวนหนึ่งที่สามารถแสดงเป็นเศษส่วนได้ ไร้เหตุผลเป็นคนที่ไม่สามารถ

อย่างไรก็ตามในทางที่ถูกเนื่องจากจุดลอยตัวมีความแม่นยำ จำกัด คุณจึงสามารถแสดงเป็นกลุ่มบางรูปแบบได้เสมอ (ฉันคิด...)


4
ลูกลอย (หรือสองเท่า) คือเศษส่วน ตัวส่วนเป็นเลขยกกำลัง 2 นั่นเป็นเหตุผลว่าทำไมพวกมันจึงไม่สามารถแทนค่าจำนวนตรรกยะได้อย่างแน่นอน
erickson

1

กรอกรหัสด้านบนและแปลงเป็น as3

public static function toFrac(f:Number) : String
    {
        if (f>1)
        {
            var parte1:int;
            var parte2:Number;
            var resultado:String;
            var loc:int = String(f).indexOf(".");
            parte2 = Number(String(f).slice(loc, String(f).length));
            parte1 = int(String(f).slice(0,loc));
            resultado = toFrac(parte2);
            parte1 *= int(resultado.slice(resultado.indexOf("/") + 1, resultado.length)) + int(resultado.slice(0, resultado.indexOf("/")));
            resultado = String(parte1) +  resultado.slice(resultado.indexOf("/"), resultado.length)
            return resultado;
        }
        if( f < 0.47 )
            if( f < 0.25 )
                if( f < 0.16 )
                    if( f < 0.13 )
                        if( f < 0.11 )
                            return "1/10";
                        else
                            return "1/9";
                    else
                        if( f < 0.14 )
                            return "1/8";
                        else
                            return "1/7";
                else
                    if( f < 0.19 )
                        return "1/6";
                    else
                        if( f < 0.22 )
                            return "1/5";
                        else
                            return "2/9";
            else
                if( f < 0.38 )
                    if( f < 0.29 )
                        return "1/4";
                    else
                        if( f < 0.31 )
                            return "2/7";
                        else
                            return "1/3";
                else
                    if( f < 0.43 )
                        if( f < 0.40 )
                            return "3/8";
                        else
                            return "2/5";
                    else
                        if( f < 0.44 )
                            return "3/7";
                        else
                            return "4/9";
        else
            if( f < 0.71 )
                if( f < 0.60 )
                    if( f < 0.56 )
                        return "1/2";
                    else
                        if( f < 0.57 )
                            return "5/9";
                        else
                            return "4/7";
                else
                    if( f < 0.63 )
                        return "3/5";
                    else
                        if( f < 0.66 )
                            return "5/8";
                        else
                            return "2/3";
            else
                if( f < 0.80 )
                    if( f < 0.74 )
                        return "5/7";
                    else
                        if(f < 0.78 )
                            return "3/4";
                        else
                            return "7/9";
                else
                    if( f < 0.86 )
                        if( f < 0.83 )
                            return "4/5";
                        else
                            return "5/6";
                    else
                        if( f < 0.88 )
                            return "6/7";
                        else
                            if( f < 0.89 )
                                return "7/8";
                            else
                                if( f < 0.90 )
                                    return "8/9";
                                else
                                    return "9/10";
    }

ขอบคุณฉันใช้สิ่งนี้สำหรับเดลฟีและง่ายต่อการพอร์ตมากกว่าสิ่งที่เป็นลอนทั้งหมด
ปีเตอร์เทอร์เนอร์

1

นี่คือการใช้งานจาวาสคริปต์ที่รวดเร็วและสกปรกซึ่งใช้วิธีการบังคับแบบดุร้าย ไม่ได้รับการปรับให้เหมาะสมเลย แต่ทำงานในช่วงเศษส่วนที่กำหนดไว้ล่วงหน้า: http://jsfiddle.net/PdL23/1/

/* This should convert any decimals to a simplified fraction within the range specified by the two for loops. Haven't done any thorough testing, but it seems to work fine.

I have set the bounds for numerator and denominator to 20, 20... but you can increase this if you want in the two for loops.

Disclaimer: Its not at all optimized. (Feel free to create an improved version.)
*/

decimalToSimplifiedFraction = function(n) {

    for(num = 1; num < 20; num++) {  // "num" is the potential numerator
        for(den = 1; den < 20; den++) {  // "den" is the potential denominator
            var multiplyByInverse = (n * den ) / num;

            var roundingError = Math.round(multiplyByInverse) - multiplyByInverse;

            // Checking if we have found the inverse of the number, 
            if((Math.round(multiplyByInverse) == 1) && (Math.abs(roundingError) < 0.01)) {
                return num + "/" + den;
            }
        }
    }
};

//Put in your test number here.
var floatNumber = 2.56;

alert(floatNumber + " = " + decimalToSimplifiedFraction(floatNumber));

สิ่งนี้ได้รับแรงบันดาลใจจากแนวทางที่ JPS ใช้


0

อย่างที่หลาย ๆ คนระบุไว้ว่าคุณไม่สามารถแปลงจุดลอยตัวกลับเป็นเศษส่วนได้ (เว้นแต่ว่ามันจะเหมือนกับ. 25) แน่นอนว่าคุณสามารถสร้างรูปแบบการค้นหาเศษส่วนจำนวนมากและใช้ตรรกะฟัซซี่บางประเภทเพื่อสร้างผลลัพธ์ที่คุณต้องการได้ อีกครั้งสิ่งนี้จะไม่แน่นอนและคุณจะต้องกำหนดขอบเขตล่างว่าคุณต้องการให้ตัวส่วนใหญ่แค่ไหน

.32 <x <.34 = 1/3 หรืออะไรประมาณนั้น



0

ฉันเจอโซลูชัน Haskell ที่หรูหราโดยเฉพาะซึ่งใช้ประโยชน์จาก anamorphism ขึ้นอยู่กับแพ็คเกจแบบแผนการเรียกซ้ำ

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts    #-}

import           Control.Applicative   (liftA2)
import           Control.Monad         (ap)
import           Data.Functor.Foldable
import           Data.Ratio            (Ratio, (%))

isInteger :: (RealFrac a) => a -> Bool
isInteger = ((==) <*>) (realToFrac . floor)

continuedFraction :: (RealFrac a) => a -> [Int]
continuedFraction = liftA2 (:) floor (ana coalgebra)
    where coalgebra x
              | isInteger x = Nil
              | otherwise = Cons (floor alpha) alpha
                  where alpha = 1 / (x - realToFrac (floor x))

collapseFraction :: (Integral a) => [Int] -> Ratio a
collapseFraction [x]    = fromIntegral x % 1
collapseFraction (x:xs) = (fromIntegral x % 1) + 1 / collapseFraction xs

-- | Use the nth convergent to approximate x
approximate :: (RealFrac a, Integral b) => a -> Int -> Ratio b
approximate x n = collapseFraction $ take n (continuedFraction x)

ถ้าคุณลองใช้ ghci มันได้ผลจริงๆ!

λ:> approximate pi 2
22 % 7
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.