Rationale Approximation

  • Moin,
    Ich suche eine effizientere Methode als Brute Force. Mein Skript funktioniert zwar, aber für große Zahlen ist BF zu langsam...

    Edit: Der Code wurde zerschossen. Da er aber sowieso überholt ist versuche ich nicht ihn zu rekonstruieren. Die Lösung ist in Post 4.

    lg
    M

  • Zitat

    How do you find the best approximations? You could do a brute force search. For example, if the maximum denominator size is N, you could try all fractions with denominators less than or equal to N. But there’s a much more efficient algorithm. The algorithm is related to the Farey sequence named after John Farey, though I don’t know whether he invented the algorithm.

    The idea is to start with two fractions, a/b = 0/1 and c/d = 1/1. We update either a/b or c/d at each step so that a/b will be the best lower bound of x with denominator no bigger than b, and c/d will be the best upper bound with denominator no bigger than d. At each step we do a sort of binary search by introducing the mediant of the upper and lower bounds. The mediant of a/b and c/d is the fraction (a+c)/(b+d) which always lies between a/b and c/d.

    Here is an implementation of the algorithm in Python. The code takes a number x between 0 and 1 and a maximum denominator size N. It returns the numerator and denominator of the best rational approximation to x using denominators no larger than N.

  • Zitat

    Here is a translation into C++ with some minor updates. You cannot really determine equality between two floating point numbers, and besides, I need accuracy to some epsilon as well as a maximum size for the numerator. Anyway, here is a C++ offering.


    Falls das ein wenig besser verständlich ist ^^

  • Thema ist damit gelöst.

    [autoit][/autoit][autoit][/autoit][autoit]

    Global $e = 2.71828182846
    Global $pi = 3.14159265359
    Global $ap = 1.20205690316

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Local $1 = FareyApprox($e, 255)
    Local $2 = FareyApprox($pi, 255)
    Local $3 = FareyApprox($ap, 255)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    ConsoleWrite('e : ' & $1[0] & '/' & $1[1] & ' Err: ' & $1[0]/$1[1] - $e & @CRLF)
    ConsoleWrite('pi : ' & $2[0] & '/' & $2[1] & ' Err: ' & $2[0]/$2[1] - $pi & @CRLF)
    ConsoleWrite('ap : ' & $3[0] & '/' & $3[1] & ' Err: ' & $3[0]/$3[1] - $ap & @CRLF)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func FareyApprox($x, $N)
    If $x > 1 Then
    Local $t = FareyApprox(1 / $x, $N)
    Return Array($t[1], $t[0])
    EndIf
    Local $a = 0, $b = 1, $c = 1, $d = 1, $m
    While ($b <= $N And $d <= $N)
    $m = ($a + $c) / ($b + $d)
    If $x = $m Then
    Return $b + $d <= $N ? Array($a + $c, $b + $d) : $d > $b ? Array($c, $d) : Array($a, $b)
    ElseIf $x > $m Then
    $a += $c
    $b += $d
    Else
    $c += $a
    $d += $b
    EndIf
    WEnd
    Return $b > $N ? Array($c, $d) : Array($a, $b)
    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func Array($x1, $x2) ; Keiner weiß warum es das nicht nativ in AutoIt gibt... ein Mysterium :(
    Local $aRet[2] = [$x1, $x2]
    Return $aRet
    EndFunc

    [/autoit]