Проверьте, является ли число рациональным в Python

Я хотел бы знать хороший способ проверить,является ли число x рациональным (два целых числа n, m существуют так, что x=n/m) в python.

в mathematica это делается функцией

Rationalize[6.75]
27/4

Я предполагаю, что этот вопрос имеет ответ на заданную точность. Существует ли общий алгоритм получения этих двух целых чисел?

7 ответов


в python >= 2.6 есть as_integer_ratio способ на поплавках:

>>> a = 6.75
>>> a.as_integer_ratio()
(27, 4)
>>> import math
>>> math.pi.as_integer_ratio()
(884279719003555, 281474976710656)

однако, из-за того, как поплавки определены в языках программирования нет иррациональных чисел.


природа чисел с плавающей точкой означает, что нет смысла Регистрация если число с плавающей запятой рационально, так как все числа с плавающей запятой действительно являются дробями вида n / 2e. Тем не менее, вы вполне можете захотеть узнать, есть ли простой дробь (одна с небольшим знаменателем, а не с большой степенью 2), которая близко аппроксимирует данное число с плавающей запятой.

Дональд Кнут обсуждает эту последнюю проблему в Искусство программирования том II. См. ответ на упражнение 4.53-39. Идея состоит в том, чтобы искать дробь с наименьшим знаменателем в пределах диапазона, расширяя конечные точки диапазона как непрерывные дроби до тех пор, пока их коэффициенты равны, а затем, когда они отличаются, принимают простейшее значение между ними. Вот довольно простая реализация в Python:

from fractions import Fraction
from math import modf

def simplest_fraction_in_interval(x, y):
    """Return the fraction with the lowest denominator in [x,y]."""
    if x == y:
        # The algorithm will not terminate if x and y are equal.
        raise ValueError("Equal arguments.")
    elif x < 0 and y < 0:
        # Handle negative arguments by solving positive case and negating.
        return -simplest_fraction_in_interval(-y, -x)
    elif x <= 0 or y <= 0:
        # One argument is 0, or arguments are on opposite sides of 0, so
        # the simplest fraction in interval is 0 exactly.
        return Fraction(0)
    else:
        # Remainder and Coefficient of continued fractions for x and y.
        xr, xc = modf(1/x);
        yr, yc = modf(1/y);
        if xc < yc:
            return Fraction(1, int(xc) + 1)
        elif yc < xc:
            return Fraction(1, int(yc) + 1)
        else:
            return 1 / (int(xc) + simplest_fraction_in_interval(xr, yr))

def approximate_fraction(x, e):
    """Return the fraction with the lowest denominator that differs
    from x by no more than e."""
    return simplest_fraction_in_interval(x - e, x + e)

и вот некоторые результаты:

>>> approximate_fraction(6.75, 0.01)
Fraction(27, 4)
>>> approximate_fraction(math.pi, 0.00001)
Fraction(355, 113)
>>> approximate_fraction((1 + math.sqrt(5)) / 2, 0.00001)
Fraction(377, 233)

любое число с конечным десятичным разложением является рациональным числом. Вы всегда можете решить, например

5.195181354985216

говоря, что это соответствует

5195181354985216 / 1000000000000000

так как поплавки и двойники имеют конечную точность, они все рациональны.


может быть, вам это будет интересно:лучшее рациональное приближение


Python использует представление с плавающей запятой, а не рациональные числа. Взгляните на стандартная библиотека fractions модуль для некоторых деталей о рациональных числах.

понаблюдайте, например, за этим, чтобы понять, почему это идет не так:

>>> from fractions import Fraction
>>> 1.1  # Uh oh.
1.1000000000000001
>>> Fraction(1.1)  # Will only work in >= Python 2.7, anyway.
Fraction(2476979795053773, 2251799813685248)
>>> Fraction(*1.1.as_integer_ratio())  # Python 2.6 compatible
Fraction(2476979795053773, 2251799813685248)

(Ах, вы хотите увидеть случай, когда он работает?)

>>> Fraction('1.1')
Fraction(11, 10)

Как вы заметили, любое число с плавающей запятой может быть преобразовано в рациональное число путем перемещения десятичной точки и деления на соответствующую степень десять.

затем вы можете удалить наибольший общий делитель из дивиденда и делителя и проверить, соответствуют ли оба эти числа типу данных по вашему выбору.


проблема с вещественными числами в языках программирования заключается в том, что они обычно определяются как функции, возвращающие конечное представление с заданной точностью (например. функция, которая принимает n в качестве аргумента и возвращает число с плавающей запятой в пределах точности 2^-n).

вы можете определенно превратить рациональное / целое число в реальное, но даже сравнение реальных значений для равенства неразрешимо (это, по сути, проблема остановки).

вы не можете сказать, является ли действительное число x рациональный: даже в математике это обычно сложно, так как вы должны найти p и q такие, что x = p/q, и это часто не конструктивно.

однако, учитывая окно точности, вы можете найти" лучшее " рациональное приближение для этой точности, используя, например, непрерывное разложение дробей. Я считаю, что это по существу то, что делает mathematica. Но в вашем примере 6.75 уже рационально. Попробуйте вместо этого с Pi.