decToFrac(3.14159265359) // pi in fraction
// get [355, 113] or simply 355 / 113

Вы когда-нибудь задумывались, как превратить куриный бульон обратно в живого цыпленка? Конечно нельзя и нельзя. И в математике тоже есть более-менее такие же задачи. Если у вас есть дробь — неважно, большая она или маленькая — то, если вы вычислите ее, вы получите десятичную дробь. Дробь может быть рациональной (совершенно делимой) или иррациональной (не вполне делимой). Независимо от того, рациональна дробь или иррациональна, проблема для нас заключается в том, что когда мы получаем десятичное число, можем ли мы превратить его обратно в дробь?

Конечно, вне этой статьи есть много сложных алгоритмов, от книг, журнальных исследований до суперкомпьютерных лабораторий. Но у нас есть только компьютер или ноутбук, и мы хотим преобразовать десятичное число в дроби, которые лучше всего представляют десятичное число. Почему я использую слово «представлять»? Потому что, когда мы получаем настолько уникальное десятичное число — среди множества альтернативных дробей, которые мы можем искать — нет уверенности, что найдется такое, которое может идеально соответствовать данному десятичному числу.

Я новичок в математике, у меня есть только любопытство и немного знаний в языке JavaScript, я хотел бы создать функцию, которая может конвертировать десятичное число обратно в дробь. Вот код JS, который я сделал:

decToFrac = dec =>
  [...Array(1000).keys()].flatMap(
    i => [...Array(1000).keys()].map(
      j => [
        i + 1, j + 1, (i + 1) / (j + 1),
        Math.abs(((i + 1) / (j + 1)) - dec)
      ]
    )
  ).sort((a, b) => a[3] - b[3])[0].slice(0, 2)

decToFrac — это функция, которая принимает десятичное число. Первым шагом этой функции является создание матрицы размером 1000 * 1000 или 1 миллион, где каждая строка представляет собой ряд чисел от 1 до 1000, а каждый столбец заполнен числами от 1 до 1000. Второй шаг , мы преобразуем каждую ячейку в матрице в массив, содержащий:

  1. индекс строки
  2. индекс столбца
  3. десятичный результат (индекс строки/индекс столбца)
  4. абсолютная разница между десятичным результатом и целью

Третий шаг — преобразовать матрицу в массив из 1 миллиона элементов и отсортировать его на основе абсолютного значения разности от наименьшего до наибольшего. Четвертый шаг — взять первую строку и вернуть первый и второй элементы, которые являются квантором и знаменателем дроби.

Конечно, вышеприведенная функция — не самый эффективный алгоритм для работы, но, по крайней мере, мы можем попробовать 1 миллион вариантов, чтобы найти, какой из них ближе всего к десятичному значению, на которое мы ориентируемся.

decToFrac( 23.689817948712386928365235 )
// get [687, 29] in 2 seconds

687 / 29 // get 23.689655172413794
         // accurate to 3 decimals

В примере первой строки мы пытаемся заменить десятичную дробь длиной 24 цифры после запятой (пожалуйста, посмотрите). В сочетании с целым числом впереди это означает, что у нас есть 26-значное число, ближайшее совпадение которого мы хотим найти в матрице, созданной функцией decToFrac. Примерно через 2 секунды консоль возвращает пару чисел, а именно 687 и 29. Это означает, что согласно этой функции JS дробь, чье десятичное значение ближе всего к нашей цели, является этой парой чисел.

Во второй строке пример того, как если мы посчитаем 687 / 29, консоль покажет результат 23.689655172413794 с 15 десятичными знаками. Если мы сравним его с десятичным числом, которое мы дали функции ранее, станет ясно, что оба десятичных числа не совсем одинаковы. Но по крайней мере эта функция способна вычислять с точностью до 3 знаков после запятой. Если ваши потребности варьируются от области экономики до строительной техники, возможно, этой трехзначной точности достаточно, чтобы удовлетворить ваши рабочие потребности. Но не ждите, что эта функция станет единственным решением для ваших исследований в области чистой математики или физики, потому что они наверняка потребуют гораздо более высокой точности.

//            16 digits     /    16 digits
decToFrac( 7528365409872894 / 9876598265498238 )
// get [747, 980] still in 2 seconds

В этом последнем примере я попытался передать функции decToFrac дробь из 16-значных чисел, разделенных на числа равной длины. Консоль выводит [747, 980], что означает, что дробь, ближайшая к приведенному выше десятичному результату, равна 747 / 980. Интересно здесь то, что первые 3 цифры исходной пары данных даже не совсем совпадают с парой из 3 цифр, сгенерированной консолью. Это разумно, так как мы сделали нашу матрицу шириной всего 1000 * 1000.

Так что, если мы хотим, чтобы эта функция выдавала более точные числа? Конечно, вы можете, просто изменив функцию decToFrac выше, расширив сгенерированную матрицу. В примере:

decToFrac = dec =>
  [...Array(10000).keys()].flatMap( // from 1,000 to 10,000
    i => [...Array(1000).keys()].map(
      j => [
        i + 1, j + 1, (i + 1) / (j + 1),
        Math.abs(((i + 1) / (j + 1)) - dec)
      ]
    )
  ).sort((a, b) => a[3] - b[3])[0].slice(0, 2)

С измененной функцией — до 10000 * 1000 — мы можем производить дроби, которые относительно более точны, потому что получаемые пары квантора и знаменателя могут иметь 4 и 3 цифры. Функция, которая изначально выполнялась всего за 2 секунды после нажатия «Enter», теперь занимает почти полминуты. Я также пытался изменить его, чтобы он представлял собой матрицу 10000 * 10000 = сто миллионов ячеек, сначала я думал, что эта функция закончит вычисления после того, как я заварю чашку кофе, но, видимо, компьютер завис и пришлось перезагрузить его снова . Поэтому, если вы хотите изменить функцию, продолжайте, в зависимости от потребностей и ресурсов вашего компьютера.

Спасибо, и пусть это будет полезно для вас.