Предполагая, что числа не содержат слишком много значащих цифр и не слишком велики, вы можете попробовать увеличивать знаменатель, пока не найдете верное решение. Это не просто брутфорс. Кроме того, следующий сценарий остается на числе, нарушающем ограничения, до тех пор, пока ничего не найдено, в надежде получить знаменатель выше быстрее, без необходимости вычислять числа c без проблем.
Он работает на основе следующей формулы:
x / y < a / b if x * b < a * y
Это означает, что знаменатель d
действителен, если:
ceil(loNum * d / loDen) * hiDen < hiNum * d
Часть ceil (...) вычисляет наименьший возможный числитель удовлетворяет ограничению нижней границы, а остальное проверяет, удовлетворяет ли она также верхней границе.
Лучше было бы работать с реальными целочисленными вычислениями, например, просто long в Java, тогда часть ceil становится:
(loNum * d + loDen - 1) / loDen
function findRatios(arr) {
let lo = [], hi = [], consecutive = 0, d = 1
for (let i = 0; i < arr.length; i++) {
let x = '' + arr[i], len = x.length, dot = x.indexOf('.'),
num = parseInt(x.substr(0, dot) + x.substr(dot + 1)) * 10,
den = Math.pow(10, len - dot),
loGcd = gcd(num - 5, den), hiGcd = gcd(num + 5, den)
lo[i] = {num: (num - 5) / loGcd, den: den / loGcd}
hi[i] = {num: (num + 5) / hiGcd, den: den / hiGcd}
}
for (let index = 0; consecutive < arr.length; index = (index + 1) % arr.length) {
if (!valid(d, lo[index], hi[index])) {
consecutive = 1
d++
while (!valid(d, lo[index], hi[index]))
d++
} else {
consecutive++
}
}
for (let i = 0; i < arr.length; i++)
console.log(Math.ceil(lo[i].num * d / lo[i].den) + ' / ' + d)
}
function gcd(x, y) {
while(y) {
let t = y
y = x % y
x = t
}
return x
}
function valid(d, lo, hi) {
let n = Math.ceil(lo.num * d / lo.den)
return n * hi.den < hi.num * d
}
findRatios([1.15, 1.92, 2.36, 2.63, 2.78, 3.14, 24.04])