【问题标题】:Finding the closest integer fraction to a given random real between 0..1, given ranges of numerator and denominator在给定的分子和分母范围内,找到最接近给定随机实数的 0..1 整数分数
【发布时间】:2011-05-22 02:21:06
【问题描述】:

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

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

【问题讨论】:

  • 到目前为止你有什么?
  • 我保持 Xi 不变,并得到最接近的 Yi。我发现我离得不够近。我知道我可以通过上下推动 Xi 并看看我得到了什么来接近,但这似乎很糟糕。
  • 乍一看似乎很容易,但我认为可能很难。如果没有像 1/2 = .5 这样的完美解决方案,则可能有多个正确答案。实际上我猜在那种情况下也有多个答案,比如 2/4。在有多个答案的情况下,我想要范围内最大的 Xi 和 Yi。
  • x[] 和 y[] 是数字列表/数组还是数字范围?

标签: algorithm math optimization mathematical-optimization fractions


【解决方案1】:

使用Farey sequence

这是一个简单且数学上漂亮的算法来解决这个问题:运行二进制搜索,在每次迭代中,下一个数字由中值公式(如下)给出。根据法雷数列的性质,该数是该区间内分母最小的数。因此,这个序列将始终收敛,并且永远不会“错过”一个有效的解决方案。

在伪代码中:

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:你为什么会期望这能起作用?请记住,分子和分母是有限制的。
  • @John:x = [5],y = [8],R = 3/5。这会输出 1 并停止(在步骤 3 中),这甚至不是一个可行的解决方案。
  • @John:我假设 x 和 y 是任意正数的数组,而这个答案假设 1
  • @DrXorile:显然仅仅使用 farey 序列不会给你正确的答案。您还需要正确使用算法。文章中的代码不正确。只需运行我的伪代码并得到 17/28。欢迎您找出不同之处。
  • @Echsecutor:因为两者都是单调增加的,所以当它们中的第一个超出界限时,就没有必要再进一步观察了。
【解决方案2】:

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

这将很快找到一个非常好的近似值,但我不确定这是否总能找到最接近的近似值。据了解

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

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

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

【讨论】:

  • 我可能会误解 - 我不想要连分数作为答案,我想要一个分子和分母。你是说如果我找到连分数,那么我就可以在更简化的分数上获得某种最优性保证?
  • 您可能想要的是“最佳有理近似值”(在连分数的维基百科页面上),它要么收敛到连分数,要么使其中一个收敛的最终商减少一个。
  • 连分数确实会产生有理近似值(通过使用最后一个收敛到足够小的分子/分母)。但是,为什么这应该是给定提名人/分母范围内 R 的最佳近似值?
【解决方案3】:

假设 R 是一个实数,例如 0 &lt;= R &lt;= 1、整数 x: [1 ... n] 和整数 y: [1 ... m]。假设n &lt;= m,因为如果n &gt; mx[n]/y[m] 将大于1,这不可能是最接近R 的近似值。

因此,R 与分母 d 的最佳近似值将是 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()

【讨论】:

  • 蛮力并不总是最好的算法 ;)
【解决方案4】:

Prolly 被激怒了,但是查找可能是最好的,我们计算每个可能值的所有小数值。因此,只需简单地索引通过小数部分索引的二维数组,数组元素包含真正的等价物。我猜我们有离散的 X 和 Y 部分,所以这是有限的,它不会反过来......啊,是的,实际的搜索部分......erm reet......

【讨论】:

  • 在我的特定应用程序中,n 和 m 大约为 100,000。这使得预计算是不可取的。我希望进行某种爬山优化。
【解决方案5】:

而不是完全暴力搜索,对最短的列表进行线性搜索,使用 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】:

解决方案: 您可以这样做 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))。这个函数可以通过一些数学规则来改进,但是会很复杂,我跳过了。

【讨论】:

  • 有什么聪明的优化比 O(nm) 空间和 O(nm lg (nm)) 时间做得更好?
  • 不,不是。尤其是没有证据。
  • @Moron,你想要证明什么?上面描述的算法按照指定的顺序运行,并且会得到最好的答案,例如对于你说证明的二进制搜索,它找到完全匹配?不,因为算法描述了信任,关于订单,很容易证明它,如果有任何歧义告诉描述它。
  • 我正在回复您对约翰的评论。不是关于你的答案。
【解决方案7】:

如果R 的分母大于m,则使用限制为m 的Farey 方法(Fraction.limit_denominator 方法实现)得到一个分数a/b,其中b 更小比m 否则让a/b = R。使用b &lt;= m,或者a &lt;= 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 方法,但我不确定:

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

【讨论】:

    猜你喜欢
    • 2017-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-25
    • 1970-01-01
    • 1970-01-01
    • 2021-05-23
    • 1970-01-01
    相关资源
    最近更新 更多