2010-11-24 42 views
7

Me gustaría saber una buena forma de verificar si un número x es un racional (dos enteros n, m existen de modo que x = n/m) en python.Comprueba si un número es racional en Python

en Mathematica, esto se hace mediante la función

Rationalize[6.75] 
27/4 

Asumo esta pregunta tiene una respuesta para una precisión dada. ¿Hay un algoritmo común para obtener estos dos enteros?

Respuesta

13

en Python> = 2.6 no es un método as_integer_ratio en los flotadores:

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

Sin embargo, debido a la forma en que se definen los flotadores en lenguajes de programación no hay números irracionales.

+0

Irracionales no pueden ser representados por razón (nal) s, ¡esa misma definición y la fuente del nombre! – delnan

+0

@delnan, FWIW, se pueden representar como racionales. Ambos métodos de uso común para hacerlo formalmente (cortes de Dedekind y series de Cauchy) usan racionales para hacerlo. Simplemente usan una cantidad infinita de racionales :) – aaronasterling

+0

Para una verdadera diversión, busca fracciones continuas. Para implementar una solución usted mismo, haga la fracción continua hasta que resulte en una fracción exacta o el denominador sea demasiado grande para su gusto. – phkahler

5

Cualquier número con una expansión decimal finita es un número racional. Siempre se puede resolver, por ejemplo,

5.195181354985216 

diciendo que corresponde a

5195181354985216/1000000000000000 

Así que ya que los flotadores y dobles tienen una precisión finita que todos son números racionales.

+1

Nota "precisión dada" en la pregunta original. –

1

Como ha notado, cualquier número de coma flotante se puede convertir a un número racional moviendo el punto decimal y dividiendo por la potencia apropiada de diez.

Puede eliminar el mayor divisor común del dividendo y del divisor y comprobar si estos dos números encajan en el tipo de datos de su elección.

+0

FWIW, puede determinar fácilmente determinar el máximo divisor común en Python usando 'fractions.gcd()'. – martineau

4

Python usa representación en coma flotante en lugar de números racionales. Eche un vistazo a the standard library fractions module para obtener más información sobre los números racionales.

Observe, por ejemplo, esto, a ver qué sale mal:

>>> 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) 

(Oh, usted quiere ver un caso en el que trabaja?)

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

¿hay lenguajes de programación que "usen" números racionales? – SilentGhost

+2

@SilentGhost: ABC lo hace. Vea las experiencias de Guido y por qué Python no lo hace: http://python-history.blogspot.com/2009/03/problem-with-integer-division.html –

+0

@SilentGhost, Common Lisp y (creo) Scheme do también. – aaronasterling

0

El problema con los bienes números en lenguajes de programación es que generalmente se definen como funciones que devuelven una representación finita dada una precisión (por ejemplo, una función que toma n como argumento y devuelve un número de punto flotante dentro de 2^-n precisión).

Definitivamente puede convertir un número racional/entero en un real, pero incluso la comparación de reales para la igualdad es indecidible (es esencialmente el problema de detención).

No puede decir si un número real x es racional: incluso en matemáticas, generalmente es difícil, ya que tiene que encontrar pyq tal que x = p/q, y esto a menudo no es constructivo.

Sin embargo, dada una ventana de precisión, puede encontrar la "mejor" aproximación racional para esta precisión utilizando, por ejemplo, la expansión de fracción continua. Creo que eso es esencialmente lo que hace la matemática. Pero en tu ejemplo, 6.75 ya es racional. Prueba con Pi en su lugar.

8

La naturaleza de los números de punto flotante significa que no tiene sentido a cheque si un número de coma flotante es racional, ya que todos los números de punto flotante son realmente fracciones de la forma n/2 correo. Sin embargo, es posible que desee saber si hay simple fracción (una con un denominador pequeño en lugar de una gran potencia de 2) que se aproxima mucho a un número de punto flotante dado.

Donald Knuth discute este último problema en The Art of Computer Programming volumen II. Vea la respuesta al ejercicio 4.53-39. La idea es buscar la fracción con el denominador más bajo dentro de un rango, expandiendo los puntos finales del rango como fracciones continuas siempre que sus coeficientes sean iguales, y luego cuando difieran, tome el valor más simple entre ellos. He aquí una aplicación bastante sencilla en 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) 

Y aquí están algunos resultados:

>>> 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) 
+0

+1 para proporcionar el algoritmo. Nitpick: debe ser "si x == y: return x", ya que parece estar considerando el intervalo cerrado [x, y]. –

+0

No hago eso porque se supone que 'x' y' y' son flotantes, mientras que se supone que la función devuelve un 'Fraction'. Creo que es mejor generar un error en este caso. –

+0

Tendría sentido devolver 'Fraction (* x.as_integer_ratio())', supongo. –

Cuestiones relacionadas