Trova le vere radici di un polinomio


24

Scrivi un programma autonomo che, quando viene dato un polinomio e un limite, troverà tutte le vere radici di quel polinomio in un errore assoluto che non supera il limite.

vincoli

So che Mathematica e probabilmente alcune altre lingue hanno una soluzione a un simbolo, e questo è noioso, quindi dovresti seguire le operazioni primitive (addizione, sottrazione, moltiplicazione, divisione).

C'è una certa flessibilità nei formati di input e output. È possibile accettare input tramite argomenti stdin o da riga di comando in qualsiasi formato ragionevole. È possibile consentire il virgola mobile o richiedere l'utilizzo di alcune rappresentazioni di numeri razionali. Puoi prendere il limite o il reciproco del limite, e se stai usando il virgola mobile puoi supporre che il limite non sarà inferiore a 2 ulp. Il polinomio dovrebbe essere espresso come un elenco di coefficienti monomiali, ma può essere big o little endian.

Devi essere in grado di giustificare il motivo per cui il tuo programma funzionerà sempre (problemi numerici modulo), sebbene non sia necessario fornire prove complete in linea.

Il programma deve gestire i polinomi con radici ripetute.

Esempio

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

L'input potrebbe essere ad es

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

L'output potrebbe essere ad es

-1.41 1.42

ma no

-1.40 1.40

in quanto ha errori assoluti di circa 0,014 ...

Casi test

Semplice:

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

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

Radice multipla:

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

Polinomio di 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 Questa domanda è stata nella Sandbox per circa 3 mesi. Se ritieni che debba essere migliorato prima di pubblicare, visita Sandbox e commenta le altre domande proposte prima che vengano pubblicate su Main.



@belisarius, ??
Peter Taylor,

3
era inteso come uno scherzo :(
Dr. belisarius,

So che questa è una vecchia sfida, quindi non sentirti obbligato a rispondere se non hai voglia di riaprirla. (a) Possiamo scrivere una funzione o solo un programma completo? (b) Nel caso in cui possiamo scrivere una funzione, possiamo supporre che l'input utilizzi un tipo di dati conveniente, ad esempio Python fractions.Fraction(un tipo razionale)? (c) Dobbiamo gestire i polinomi di grado <1? (d) Possiamo presumere che il coefficiente principale sia 1?
Ell

(e) Per quanto riguarda i polinomi con radici ripetute, vale la pena fare una distinzione tra radici di molteplicità dispari e pari (i casi di test hanno solo radici di molteplicità dispari.) Mentre le radici di molteplicità dispari non sono troppo difficili da affrontare, io ' Non sono sicuro di quanto sia tangibile gestire numericamente correttamente anche le radici della molteplicità, soprattutto perché si specifica solo un margine di errore per i valori delle radici, non per la loro esistenza. (...)
Ell

Risposte:


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&]]

Questa soluzione implementa il metodo Durand-Kerner per risolvere i polinomi. Nota che questa non è una soluzione completa (come verrà mostrato di seguito) perché non riesco ancora a gestire il Polinomio di Wilkinson con la precisione specificata. Innanzitutto una spiegazione di ciò che sto facendo: codice in formattazione matematica

#[[i]]-p[#[[i]]]/Product[If[i!=j,#[[i]]-#[[j]],1],{j,l}]&: Pertanto la funzione calcola per ciascun indice ila prossima approssimazione di Durand-Kerner. Quindi questa linea viene incapsulata in una tabella e applicata utilizzando un NestWhile ai punti di input generati da Table[(0.9+0.1*I)^i,{i,l}]. La condizione su NestWhile è che la variazione massima (su tutti i termini) da una iterazione alla successiva è maggiore della precisione specificata. Quando tutti i termini sono cambiati meno di questo, NestWhile termina e Re@Selectrimuove gli zeri che non rientrano nella linea reale.

Esempio di output:

> 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.}

Come probabilmente vedrai, quando il grado aumenta, questo metodo inizia a rimbalzare attorno ai valori corretti, senza mai avvicinarsi completamente. Se imposto la condizione di arresto del mio codice su qualcosa di più rigoroso di "da un'iterazione alla successiva, le ipotesi cambiate di non più di epsilon", l'algoritmo non si ferma mai. Immagino che dovrei usare Durand-Kerner come input per il metodo di Newton?


Durand-Kerner ha anche potenziali problemi con radici multiple. (Anche il metodo di Newton potrebbe non essere di grande aiuto - il polinomio di Wilkinson è stato scelto specificamente per essere mal condizionato).
Peter Taylor,

Hai ragione: ho abbandonato quel corso di azione dopo aver ingrandito la x = 17 di Wilkinson, è un casino assoluto. Temo che dovrò cercare una soluzione simbolica con una base Groebner per ottenere molta più precisione.
Kaya,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.