簡體   English   中英

在給定分子和分母的范圍內,在 0..1 之間找到與給定隨機實數最接近的整數分數

[英]Finding the closest integer fraction to a given random real between 0..1, given ranges of numerator and denominator

給定兩個范圍的正整數x: [1 ... n]y: [1 ... m]和從 0 到 1 的隨機實數 R,我需要從 x 和y 使得 x_i / y_j 最接近 R。

找到這對的最有效方法是什么?

使用Farey 序列

這是一個簡單且數學上漂亮的算法來解決這個問題:運行二分搜索,在每次迭代中,下一個數字由中位數公式(如下)給出。 根據法里數列的性質,該數是該區間內分母最小的數。 因此,這個序列將始終收斂,並且永遠不會“錯過”一個有效的解決方案。

在偽代碼中:

input: m, n, R

a_num = 0, a_denom = 1
b_num = 1, b_denom = 1

repeat:
    -- interestingly c_num/c_denom is already in reduced form
    c_num = a_num + b_num
    c_denom = a_denom + b_denom

    -- if the numbers are too big, return the closest of a and b
    if c_num > n or c_denom > m then
        if R - a_num/a_denom < b_num/b_denom - R then
            return a_num, a_denom
        else
            return b_num, b_denom

    -- adjust the interval:
    if c_num/c_denom < R then
        a_num = c_num, a_denom = c_denom
    else
        b_num = c_num, b_denom = c_denom

goto repeat

即使它的平均速度很快(我有根據的猜測它是O(log max(m,n)) ),但如果 R​​ 接近具有小分母的分數,它仍然可能很慢。 例如,在m = n = 1000000找到1/1000000的近似值將需要一百萬次迭代。

用有理數逼近實數的標准方法是計算連分數級數(參見 [1])。 在計算部分系列時對分母和分母設置限制,並且在您突破限制之前的最后一個值是非常接近您的實數的分數。

這將很快找到一個非常好的近似值,但我不確定這是否總能找到最接近的近似值。 眾所周知

任何收斂[連分數展開的部分值]比分母小於收斂分母的任何其他分數更接近連分數

但是可能存在分母較大(仍低於您的極限)的近似值,它們是更好的近似值,但不是收斂的。

[1] http://en.wikipedia.org/wiki/Continued_fraction

鑒於 R 是一個實數,使得0 <= R <= 1 ,整數x: [1 ... n]和整數y: [1 ... m] 假設n <= m ,因為如果n > m那么x[n]/y[m]將大於1 ,這不可能是最接近R近似值。

因此,分母為 d 的 R 的最佳近似值將是floor(R*d) / dceil(R*d) / d

該問題可以在O(m)時間和O(1)空間內解決(在 Python 中):

from __future__ import division
from random import random
from math import floor

def fractionize(R, n, d):
    error = abs(n/d - R)
    return (n, d, error)  # (numerator, denominator, absolute difference to R)

def better(a, b):
    return a if a[2] < b[2] else b

def approximate(R, n, m):
    best = (0, 1, R)
    for d in xrange(1, m+1):
        n1 = min(n, int(floor(R * d)))
        n2 = min(n, n1 + 1) # ceil(R*d)
        best = better(best, fractionize(R, n1, d))
        best = better(best, fractionize(R, n2, d))
    return best

if __name__ == '__main__': 
    def main():
        R = random()
        n = 30
        m = 100
        print R, approximate(R, n, m)
    main()

Prolly 被激怒了,但在我們計算每個可能值的所有小數值時,查找可能是最好的。 我想我們有離散的 X 和 Y 部分,所以這是有限的,它不會反過來......啊,是的,實際的搜索部分......erm reet......

解決方案:您可以使用O(1)空間和O(m log(n))時間:

無需創建任何列表進行搜索,

偽代碼可能有問題,但想法是這樣的:

r: input number to search.
n,m: the ranges.

for (int i=1;i<=m;i++)
{
    minVal = min(Search(i,1,n,r), minVal);
}

//x and y are start and end of array:
decimal Search(i,x,y,r)
{
   if (i/x > r)
      return i/x - r;

   decimal middle1 = i/Cill((x+y)/2); 
   decimal middle2 = i/Roof((x+y)/2);

   decimal dist = min(middle1,middle2)

   decimal searchResult = 100000;

   if( middle > r)
     searchResult = Search (i, x, cill((x+y)/2),r)
  else
     searchResult = Search(i, roof((x+y)/2), y,r)

  if  (searchResult < dist)
     dist = searchResult;

  return dist;
}

找到索引作為家庭作業給讀者。

描述:我想你可以通過代碼理解這個想法,但是讓我們跟蹤一個 for 循環:當 i=1 時:

你應該在下面的數字中搜索: 1,1/2,1/3,1/4,....,1/n 你用 (1,1/cill(n/2)) 和 (1/ floor(n/2), 1/n) 並對其進行類似的二分搜索以找到最小的。

應該對所有項目執行此 for 循環,因此它將完成m次。 並且每次都需要 O(log(n))。 這個函數可以通過一些數學規則來改進,但是它會很復雜,我跳過它。

不是完全蠻力搜索,而是對最短的列表進行線性搜索,使用 round 找到每個元素的最佳匹配。 也許是這樣的:

best_x,best_y=(1,1)
for x in 1...n:
    y=max(1,min(m,round(x/R)))
    #optional optimization (if you have a fast gcd)
    if gcd(x,y)>1:
        continue

    if abs(R-x/y)<abs(R-bestx/besty):
        best_x,best_y=(x,y)
return (best_x,best_y)

完全不確定gcd “優化”是否會更快......

如果R的分母大於m則使用 Farey 方法( Fraction.limit_denominator方法實現),限制為m以獲得分數a/b ,其中b小於m否則讓a/b = R 使用b <= m ,要么a <= n就完成了,要么讓M = math.ceil(n/R)並重新運行 Farey 方法。

def approx2(a, b, n, m):
    from math import ceil
    from fractions import Fraction
    R = Fraction(a, b)
    if R < Fraction(1, m):
        return 1, m
    r = R.limit_denominator(m)
    if r.numerator > n:
        M = ceil(n/R)
        r = R.limit_denominator(M)
    return r.numerator, r.denominator

>>> approx2(113, 205, 50, 200)
(43, 78)

使用min(ceil(n/R), m)的限制分母可能只運行一次 Farey 方法min(ceil(n/R), m)但我不確定:

def approx(a, b, n, m):
    from math import ceil
    from fractions import Fraction
    R = Fraction(a, b)
    if R < Fraction(1, m):
        return 1, m
    r = R.limit_denominator(min(ceil(n/R), m))
    return r.numerator, r.denominator

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM