Нахождение ближайшей целочисленной дроби к заданному случайному вещественному числу между 0..1, заданным диапазонам числителя и знаменателя
Учитывая два диапазона положительных целых чисел x: [1... n]
и y: [1... m]
и случайного вещественного числа R от 0 до 1, мне нужно найти пару элементов (i, j) из x и у такой, что x_i/y_j ближе всего к R.
Какой самый эффективный способ найти эту пару?
Ответы
Ответ 1
Используйте последовательность Фарея.
- Начните с a = 0, b = 1 и A = {ближайший из a и b к R}.
- Пусть c будет следующей дробью Фари между a и b, заданной как c = (num (a) + num (b))/(denom (a) + denom (b)) (обязательно делите num (c) и denom (c) gcd (num (c), denom (c))):
![enter image description here]()
- Если числитель или знаменатель c выходит за пределы диапазона ввода, выведите A и остановите.
- Если c ближе к R, чем A, установите A на c.
- Если R находится в [a, c], установите b = c, в противном случае установите a = c.
- Перейти к 2.
Это находит наилучшее приближение в пространстве O (1), наихудшем времени O (M) и в среднем O (log M).
Ответ 2
Стандартным подходом к аппроксимации реалов рациональными является вычисление серии цепных дробей (см. [1]). Положите предел на номинатор и знаменатель при вычислении частей серии, а последнее значение до того, как вы нарушите ограничения, - это фракция, очень близкая к вашему действительному числу.
Это очень хорошее приближение очень быстро, но я не уверен, что это всегда найдет самое близкое приближение. Известно, что
любое сходящееся [частичное значение продолжения дробных фракций] ближе к продолженной дроби, чем любая другая фракция, знаменатель которой меньше, чем у сходящегося
но могут быть приближения с большим знаменателем (все еще ниже вашего предела), которые являются лучшими приближениями, но не являются сходящимися.
[1] http://en.wikipedia.org/wiki/Continued_fraction
Ответ 3
Учитывая, что R - вещественное число, такое, что 0 <= R <= 1
, целые числа x: [1 ... n]
и целые числа y: [1 ... m]
. Предполагается, что n <= m
, так как если n > m
, то x[n]/y[m]
будет больше, чем 1
, которое не может быть ближайшим приближением к R
.
Следовательно, наилучшее приближение R с знаменателем d будет либо floor(R*d) / d
, либо ceil(R*d) / d
.
Проблема может быть решена в O(m)
времени и O(1)
space (в 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 get flamed, но поиск может быть лучшим, когда мы вычисляем все дробные значения для каждого из возможных значений. Таким образом, просто индексирование массива 2d, индексированного через дробные части с элементом массива, содержащим реальный эквивалент. Я предполагаю, что у нас есть отдельные части X и Y, поэтому это конечно, это было бы не так. Ahh да, фактическая часть поиска.... erm reet....
Ответ 5
Решение:
Вы можете сделать это 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:
когда я = 1:
вы должны искать в следующих номерах:
1,1/2,1/3,1/4,...., 1/п
вы проверяете число с помощью (1,1/cill (n/2)) и (1/floor (n/2), 1/n) и выполняете аналогичный двоичный поиск на нем, чтобы найти наименьший.
Должно сделать это для цикла для всех элементов, поэтому это будет сделано м. и в каждый раз он принимает O (log (n)). эта функция может улучшить некоторые математические правила, но это будет сложно, я пропущу ее.
Ответ 6
Вместо поиска полностью грубой силы выполните линейный поиск по кратчайшему из ваших списков, используя раунд, чтобы найти наилучшее соответствие для каждого элемента. Может быть, что-то вроде этого:
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
"быстрее"...