在给定的分子和分母范围内找到0..1之间给定随机实数的最接近整数分数

时间:2010-12-08 08:43:47

标签: algorithm math optimization mathematical-optimization fractions

给定两个正整数范围x: [1 ... n]y: [1 ... m]以及从0到1的随机实数R,我需要从x和y找到一对元素(i,j),使得x_i / y_j最接近R。

找到这一对的最有效方法是什么?

7 个答案:

答案 0 :(得分:11)

使用Farey sequence

  1. 以a = 0开头,b = 1且A = {最接近a,b至R}。
  2. 设c是a和b之间的下一个Farey分数,由c =(num(a)+ num(b))/(denom(a)+ denom(b))给出(确保除以num(c) )和denom(c)由gcd(num(c),denom(c))): enter image description here
  3. 如果c的分子或分母超出输入范围,则输出A并停止。
  4. 如果c比A更接近R,则将A设为c。
  5. 如果R在[a,c]中,则设置b = c,否则设置a = c。
  6. 转到2.
  7. 这在O(1)空间,O(M)时间最差情况和O(log M)平均值中找到最佳近似值。

答案 1 :(得分:6)

用有理数逼近实数的标准方法是计算连续分数系列(见[1])。在计算系列的部分时限制分母和分母,并且在突破限制之前的最后一个值是非常接近实数的分数。

这将非常快速地找到非常好的近似值,但我不确定这总是会找到最接近的近似值。众所周知

  

任何收敛[连续分数展开的部分值]比分母小于收敛分数的任何其他分数更接近连续分数

但是可能存在较大分母(仍低于极限)的近似值,这些近似值是更好的近似值,但不是收敛值。

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

答案 2 :(得分:1)

假设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()

答案 3 :(得分:0)

Prolly变得火热,但是在我们计算每个可能值的所有小数值时查找可能是最好的。因此,简单地索引通过小数部分索引的2d数组,其中数组元素包含实际等价物。我想我们有离散的X和Y部分所以这是有限的,它不会是相反的方式....啊,是的,实际的搜索部分....呃reet ....

答案 4 :(得分:0)

解决方案: 你可以这样做 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)检查数字并对其进行类似的二元搜索以找到最小的数字。

应该为所有项目循环执行此操作,因此它将完成 m 时间。并且每次都需要O(log(n))。这个函数可以通过一些数学规则来改进,但它会很复杂,我跳过它。

答案 5 :(得分:0)

不是完全强力搜索,而是在列表中最短的位置进行线性搜索,使用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“优化”是否会更快......

答案 6 :(得分:0)

如果 R 的分母大于 m,则使用 Farey 方法(Fraction.limit_denominator 方法实现),限制为 m 得到分数 { {1}} 其中 a/b 小于 b 否则让 m。使用 a/b = R,要么 b <= m 完成,否则让 a <= n 重新运行 Farey 方法。

M = math.ceil(n/R)

可能可以使用 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) 的限制分母只运行一次 Farey 方法,但我不确定:

min(ceil(n/R), m)