Encuentra raíces reales de un polinomio

24

Escriba un programa autónomo que, cuando se le da un polinomio y un límite, encontrará todas las raíces reales de ese polinomio en un error absoluto que no exceda el límite.

Restricciones

Sé que Mathematica y probablemente algunos otros idiomas tienen una solución de un solo símbolo, y eso es aburrido, por lo que debe atenerse a las operaciones primitivas (suma, resta, multiplicación, división).

Hay cierta flexibilidad en los formatos de entrada y salida. Puede recibir información a través de stdin o argumentos de línea de comandos en cualquier formato razonable. Puede permitir la coma flotante o requerir que se use alguna representación de números racionales. Puede tomar el límite o el recíproco del límite, y si está utilizando coma flotante, puede suponer que el límite no será inferior a 2 ulp. El polinomio debe expresarse como una lista de coeficientes monomiales, pero puede ser big o little endian.

Debe poder justificar por qué su programa siempre funcionará (problemas de módulo numérico), aunque no es necesario proporcionar pruebas completas en línea.

El programa debe manejar polinomios con raíces repetidas.

Ejemplo

x^2 - 2 = 0 (error bound 0.01)

La entrada podría ser, por ejemplo

-2 0 1 0.01
100 1 0 -2
1/100 ; x^2-2

La salida podría ser, por ejemplo

-1.41 1.42

pero no

-1.40 1.40

ya que tiene errores absolutos de aproximadamente 0.014 ...

Casos de prueba

Simple:

x^2 - 2 = 0 (error bound 0.01)

x^4 + 0.81 x^2 - 0.47 x + 0.06 (error bound 10^-6)

Raíz múltiple:

x^4 - 8 x^3 + 18 x^2 - 27 (error bound 10^-6)

El polinomio de Wilkinson:

x^20 - 210 x^19 + 20615 x^18 - 1256850 x^17 + 53327946 x^16 -1672280820 x^15 +
    40171771630 x^14 - 756111184500 x^13 + 11310276995381 x^12 - 135585182899530 x^11 +
    1307535010540395 x^10 - 10142299865511450 x^9 + 63030812099294896 x^8 -
    311333643161390640 x^7 + 1206647803780373360 x^6 -3599979517947607200 x^5 +
    8037811822645051776 x^4 - 12870931245150988800 x^3 + 13803759753640704000 x^2 -
    8752948036761600000 x + 2432902008176640000  (error bound 2^-32)

NB Esta pregunta estuvo en el Sandbox durante aproximadamente 3 meses. Si cree que fue necesario mejorar antes de publicar, visite el Sandbox y comente las otras preguntas propuestas antes de que se publiquen en Main.

Peter Taylor
fuente
"Debe poder justificar por qué su programa siempre funcionará" En caso de que alguien necesite ayuda con eso
Dr. belisarius
@belisarius, ??
Peter Taylor
3
fue una broma :(
Dr. belisarius
Sé que este es un viejo desafío, así que no se sienta obligado a responder si no tiene ganas de volver a abrirlo. (a) ¿Podemos escribir una función, o solo un programa completo? (b) En caso de que podamos escribir una función, ¿podemos suponer que la entrada utiliza algún tipo de datos conveniente, por ejemplo, Python fractions.Fraction(un tipo racional)? (c) ¿Tenemos que manejar polinomios de grado <1? (d) ¿Podemos suponer que el coeficiente principal es 1?
Ell
(e) Con respecto a los polinomios con raíces repetidas, vale la pena hacer una distinción entre raíces de multiplicidades pares e impares (los casos de prueba solo tienen raíces de multiplicidades impares). Si bien las raíces de multiplicidad impar no son demasiado difíciles de tratar, I ' No estoy seguro de lo tangible que es manejar correctamente las raíces de multiplicidad incluso numéricamente, especialmente porque solo especifica un margen de error para los valores de las raíces, no para su existencia. (...)
Ell

Respuestas:

8

Mathematica, 223

r[p_,t_]:=Module[{l},l=Exponent[p[x],x];Re@Select[NestWhile[Table[#[[i]]-p[#[[i]]]/Product[If[i!=j,#[[i]]-#[[j]],1],{j,l}],{i,l}]&,Table[(0.9+0.1*I)^i,{i,l}],2*Max[Table[Abs[#1[[i]]-#2[[i]]],{i,l}]]>t&,2],Abs[Im[#]]<t^.5&]]

Esta solución implementa el método Durand-Kerner para resolver polinomios. Tenga en cuenta que esta no es una solución completa (como se mostrará a continuación) porque todavía no puedo manejar el polinomio de Wilkinson con la precisión especificada. Primero una explicación de lo que estoy haciendo: código en formato matemático

#[[i]]-p[#[[i]]]/Product[If[i!=j,#[[i]]-#[[j]],1],{j,l}]&: Por lo tanto, la función calcula para cada índice ila próxima aproximación de Durand-Kerner. Luego, esta línea se encapsula en una Tabla y se aplica usando un NestWhile a los puntos de entrada generados por Table[(0.9+0.1*I)^i,{i,l}]. La condición en NestWhile es que el cambio máximo (en todos los términos) de una iteración a la siguiente es mayor que la precisión especificada. Cuando todos los términos han cambiado menos que esto, NestWhile finaliza y Re@Selectelimina los ceros que no se encuentran en la línea real.

Salida de ejemplo:

> p[x_] := x^2 - 2
> r[p, .01]
{1.41421, -1.41421}

> p[x_] := x^4 - 8 x^3 + 18 x^2 - 27
> r[p, 10^-6]
{2.99999, 3., 3.00001, -1.}

> p[x_] := x^20 - 210 x^19 + ... + 2432902008176640000 (Wilkinson's)
> Sort[r[p, 2^-32]]
{1., 2., 3., 4., 5., 6., 7.00001, 7.99994, 9.00018, 10.0002, 11.0007, \
11.9809, 13.0043, 14.0227, 14.9878, 16.0158, 16.9959, 17.9992, \
19.0001, 20.}

Como probablemente pueda ver, cuando el grado aumenta, este método comienza a rebotar alrededor de los valores correctos, nunca realmente se dirige completamente. Si configuro la condición de detención de mi código para que sea más estricta que "de una iteración a la siguiente, las suposiciones no cambian más que epsilon", entonces el algoritmo nunca se detiene. ¿Supongo que debería usar Durand-Kerner como entrada al método de Newton?

Kaya
fuente
Durand-Kerner también tiene problemas potenciales con múltiples raíces. (El método de Newton tampoco podría ayudar mucho: el polinomio de Wilkinson se elige específicamente para que esté mal acondicionado).
Peter Taylor
Tienes toda la razón: abandoné ese curso de acción después de acercarme a Wilkinson cerca de x = 17, es un desastre absoluto. Me preocupa tener que buscar una solución simbólica basada en Groebner para obtener mucha más precisión.
Kaya