Genera frattali Newton


24

Conoscete tutti il ​​metodo Newton per approssimare le radici di una funzione, vero? Il mio obiettivo in questo compito è di introdurti in un aspetto interessante di questo algoritmo.

L'algoritmo di Newton converge solo per determinati, ma soprattutto valori di input complessi. Se immagini la convergenza del metodo per tutti i valori di input sul piano complesso, di solito ottieni un bellissimo frattale come questo:

Frattale di Newton per f (x) = x ^ 3-1 Immagine dai beni comuni di Wikimedia

specificazioni

L'obiettivo di questo compito è generare tali frattali. Ciò significa che ottieni un polinomio come input e devi stampare il frattale corrispondente come immagine in un formato a tua scelta come output.

Ingresso

L'input è un elenco separato da spazi bianchi di numeri complessi. Essi sono svalutate in stile <Real part><iImaginary part>, come questo numero: 5.32i3.05. Si può presumere che il numero di input non abbia più di 4 cifre decimali e sia inferiore a 1000. Il primo di essi non deve essere zero. Ad esempio, questo potrebbe essere un input per il tuo programma:

1 -2i7,5 23.0004i-3,8 i12 0 5.1233i0.1

I numeri sono interpretati come i coefficienti di un polinomio, a partire dalla massima potenza. Per tutto il resto di questa descrizione, il polinomio di ingresso viene chiamato P . L'input sopra è uguale a questo polinomio:

f (x) = x 5 + (-2 + 7.5 i ) x 4 + (23.0004 - 3.8 i ) x 3 + 12 i x 2 + 5.1233 + 0.1 i

L'input può provenire da stdin, da un argomento passato al programma o da un prompt visualizzato nel programma. Si può presumere che l'input non contenga caratteri di spazi bianchi iniziali o finali.

interpretazione

Devi eseguire il rendering del frattale nel modo seguente:

  • Scegli tanti colori quante le radici di P e un colore in più per la divergenza
  • Per ogni numero nel piano visibile, determinare se il metodo converge e se sì in quale radice. Colora il punto in base al risultato.
  • Non stampare righelli o altre cose fantasiose
  • Stampa un punto nero nei punti, che sono le radici dei polinomi per l'orientamento. È possibile stampare fino a quattro pixel attorno a ciascuna radice.
  • Trova un modo per scegliere il piano visibile in un modo, in modo che tutte le radici siano distinguibili e si diffondano ampiamente su di esso, se possibile. Sebbene non sia necessario un perfetto posizionamento del frame di output, mi riservo il diritto di rifiutare di accettare una risposta che scelga il frame in modo inaccettabile, ad es. sempre alle stesse coordinate, tutte le radici sono in un punto, ecc.
  • L'immagine in uscita dovrebbe avere una dimensione di 1024 * 1024 pixel.
  • Il tempo di rendering è massimo di 10 minuti
  • L'uso di valori a virgola mobile a precisione singola è sufficiente

Produzione

L'output dovrebbe essere un'immagine grafica raster in un formato di file a tua scelta, leggibile da software standard per un sistema operativo di marca X. Se si desidera utilizzare un formato raro, prendere in considerazione l'aggiunta di un collegamento a un sito Web in cui è possibile scaricare un visualizzatore per esso.

Invia il file a stdout. Se la tua lingua non supporta l'inserimento di qualcosa in stdout o se trovi questa opzione meno conveniente, trova un altro modo. In ogni caso, deve essere possibile salvare l'immagine generata.

restrizioni

  • Nessuna libreria di elaborazione delle immagini
  • Nessuna libreria generatrice di frattali
  • Vince il codice più corto

estensioni

Se ti piace questa attività, puoi provare a colorare i punti in base alla velocità di convergenza o ad altri criteri. Mi piacerebbe vedere alcuni risultati interessanti.


6
Non sono del tutto sicuro se questo sia adatto come codice golf. Ai miei occhi il compito è troppo complesso. Tuttavia, potrei essere smentito.
Joey,

5
@Joey: Davvero. Vorrei che questa fosse una sfida del codice .
Joey Adams,

2
... o PPM per quella materia.
Joey,

1
@Joey: la mia intenzione era quella di creare un compito piuttosto difficile, poiché a molte persone non piacciono i compiti molto facili.
FUZxxl,

1
È facilmente suddiviso in attività separate e se la tua lingua supporta nativamente numeri in virgola mobile complessi, puoi salvare un grosso pezzo. Ho una versione non completamente giocata a 1600 caratteri, di cui 340 sono la classe numerica complessa. Non identifica ancora le radici e usa i colori, ma sto cercando di rintracciare ciò che presumo sia un bug nel codice NR. (Trovare una radice di x ^ 3-1 a partire da -0.5 + 0.866i sicuramente non dovrebbe divergere!)
Peter Taylor,

Risposte:


13

Python, 827 777 caratteri

import re,random
N=1024
M=N*N
R=range
P=map(lambda x:eval(re.sub('i','+',x)+'j'if 'i'in x else x),raw_input().split())[::-1]
Q=[i*P[i]for i in R(len(P))][1:]
E=lambda p,x:sum(x**k*p[k]for k in R(len(p)))
def Z(x):
 for j in R(99):
  f=E(P,x);g=E(Q,x)
  if abs(f)<1e-9:return x,1
  if abs(x)>1e5or g==0:break
  x-=f/g
 return x,0
T=[]
a=9e9
b=-a
for i in R(999):
 x,f=Z((random.randrange(-9999,9999)+1j*random.randrange(-9999,9999))/99)
 if f:a=min(a,x.real,x.imag);b=max(b,x.real,x.imag);T+=[x]
s=b-a
a,b=a-s/2,b+s/2
s=b-a
C=[[255]*3]*M
H=lambda x,k:int(x.real*k)+87*int(x.imag*k)&255
for i in R(M):
 x,f=Z(a+i%N*s/N+(a+i/N*s/N)*1j)
 if f:C[i]=H(x,99),H(x,57),H(x,76)
for r in T:C[N*int(N*(r.imag-a)/s)+int(N*(r.real-a)/s)]=0,0,0
print'P3',N,N,255
for c in C:print'%d %d %d'%c

Trova i limiti di visualizzazione (e le radici) trovando punti di convergenza per un gruppo di campioni casuali. Quindi disegna il grafico calcolando i punti di convergenza per ciascun punto iniziale e utilizzando una funzione hash per ottenere colori casuali per ciascun punto di convergenza. Guarda molto da vicino e puoi vedere le radici segnate.

Ecco il risultato per il polinomio di esempio.

risultato ad esempio polinomio


Buono! Mi piace questo.
FUZxxl,

14

Java, 1093 1058 1099 1077 caratteri

public class F{double r,i,a,b;F(double R,double I){r=R;i=I;}F a(F c){return
new F(r+c.r,i+c.i);}F m(F c){return new F(r*c.r-i*c.i,r*c.i+i*c.r);}F
r(){a=r*r+i*i;return new F(-r/a,i/a);}double l(F c){a=r-c.r;b=i-c.i;return
Math.sqrt(a*a+b*b);}public static void main(String[]a){int
n=a.length,i=0,j,x,K=1024,r[]=new int[n];String o="P3\n"+K+" "+K+"\n255 ",s[];F z=new
F(0,0),P[]=new F[n],R[]=new F[n],c,d,e,p,q;for(;i<n;)P[i]=new
F((s=a[i++].split("i"))[0].isEmpty()?0:Float.parseFloat(s[0]),s.length==1?0:Float.parseFloat(s[1]));double
B=Math.pow(P[n-1].m(P[0].r()).l(z)/2,1./n),b,S;for(i=1;i<n;){b=Math.pow(P[i].m(P[i-1].r()).l(z),1./i++);B=b>B?b:B;}S=6*B/K;for(x=0;x<K*K;){e=d=c=new
F(x%K*S-3*B,x++/K*S-3*B);for(j=51;j-->1;){p=P[0];q=p.m(new
F(n-1,0));for(i=1;i<n;){if(i<n-1)q=q.m(c).a(P[i].m(new
F(n-1-i,0)));p=p.m(c).a(P[i++]);}c=c.a(d=q.r().m(p));if(d.l(z)<S/2)break;}i=j>0?0:n;for(;i<n;i++){if(R[i]==null)R[i]=c;if(R[i].l(c)<S)break;}i=java.awt.Color.HSBtoRGB(i*1f/n,j<1||e.l(c)<S&&r[i]++<1?0:1,j*.02f);for(j=0;j++<3;){o+=(i&255)+" ";i>>=8;}System.out.println(o);o="";}}}

L'input è argomenti della riga di comando, ad es java F 1 0 0 -1. Run . L'output è stdout in formato PPM (ASCII pixmap).

La scala viene scelta usando il Fujiwara legato al valore assoluto delle radici complesse di un polinomio; Quindi moltiplico quello legato per 1,5. Regolo la luminosità in base al tasso di convergenza, quindi le radici saranno nelle zone più luminose. Pertanto è logico usare il bianco piuttosto che il nero per contrassegnare le posizioni approssimative delle radici (il che mi costa 41 caratteri per qualcosa che non può nemmeno essere fatto "correttamente". Se etichetta tutti i punti che convergono entro 0,5 pixel da loro stessi allora alcune radici escono senza etichetta; se etichetta tutti i punti che convergono entro 0,6 pixel da loro stessi allora alcune radici escono etichettate su più di un pixel; quindi per ogni radice etichetta il primo punto incontrato per convergere entro 1 pixel da se stesso ).

Immagine per il polinomio di esempio (convertito in png con GIMP): Radici di x ^ 5 + (- 2 + 7.5i) x ^ 4 + (23.0004-3.8i) x ^ 3 + 12i x ^ 2 + (5.1233 + 0.1i)


@FUZxxl, l'immagine è della vecchia versione. Ne caricherò uno con il tasso di convergenza più tardi. Ma il problema con la marcatura delle radici è determinare quale pixel contrassegnare. È il classico problema che con il virgola mobile non è possibile utilizzare test di uguaglianza esatti, quindi è necessario confrontare con un epsilon. Di conseguenza, non ho valori "canonici" per le radici. Potrei contrassegnare i pixel che convergono in un unico passaggio, ma ciò non garantisce di contrassegnare nulla e potrebbe contrassegnare un blocco di 4 pixel per una singola radice.
Peter Taylor,

@Peter Taylor: Come vedi, Keith Randall ha anche trovato una soluzione a questo problema. Ho aggiunto questo requisito come ulteriore difficoltà. Un approccio per farlo sarebbe quello di calcolare il pixel più vicino per ogni radice e quindi verificare che ogni pixel sia uguale a esso.
FUZxxl,

@FUZxxl, non hai capito il mio punto. "Il pixel più vicino" di una radice non è ben definito. Tuttavia, posso hackerare qualcosa in cui potrebbe funzionare per tutti i casi di test che ci provi e ho l'impressione che ciò ti renderà felice. Lo colorerò bianco, non nero, perché è più logico.
Peter Taylor,

@Peter Taylor: Ok.
FUZxxl,

6
La mia foto del profilo dovrebbe presto cambiare x^6-9x^3+8, progettata attentamente scegliendo le radici e quindi usando Wolfram Alpha per semplificare il polinomio. Ok, ho tradito scambiando le tonalità in seguito in GIMP.
Peter Taylor,

3

Python, 633 byte

import numpy as np
import matplotlib.pyplot as plt
from colorsys import hls_to_rgb
def f(z):
    return (z**4 - 1)
def df(z):
    return (4*z**3) 
def cz(z):
    r = np.abs(z)
    arg = np.angle(z)   
    h = (arg + np.pi)  / (3 * np.pi)
    l = 1.0 - 1.0/(1.0 + r**0.1)
    s = 0.8 
    c = np.vectorize(hls_to_rgb) (h,l,s)
    c = np.array(c)
    c = c.swapaxes(0,2) 
    return c    
x, y = np.ogrid[-1.5:1.5:2001j, -1.5:1.5:2001j]
z = x + 1j*y    
for i in range(10):
    z -= (f(z) / df(z))
zz = z
zz[np.isnan(zz)]=0
zz=cz(zz)
plt.figure()
plt.imshow(zz, interpolation='nearest')
plt.axis('off')
plt.savefig('plots/nf.svg')
plt.close()

Dopo accelerazioni e abbellimento (756 byte)

import numpy as np
from numba import jit
import matplotlib.pyplot as plt
from colorsys import hls_to_rgb 

@jit(nopython=True, parallel=True, nogil=True)
def f(z):
    return (z**4 - 1)   

@jit(nopython=True, parallel=True, nogil=True)
def df(z):
    return (4*z**3) 

def cz(z):
    r = np.abs(z)
    arg = np.angle(z)   

    h = (arg + np.pi)  / (3 * np.pi)
    l = 1.0 - 1.0/(1.0 + r**0.1)
    s = 0.8 

    c = np.vectorize(hls_to_rgb) (h,l,s)
    c = np.array(c)
    c = c.swapaxes(0,2) 
    return c    

x, y = np.ogrid[-1.5:1.5:2001j, -1.5:1.5:2001j]
z = x + 1j*y    

for i in range(10):
    z -= (f(z) / df(z))

zz = z
zz[np.isnan(zz)]=0
zz=cz(zz)
plt.figure()
plt.imshow(zz, interpolation='nearest')
plt.axis('off')
plt.savefig('plots/nf.svg')
plt.close()

Il diagramma seguente è per la funzione Newton Fractal of log (z).

Newton Fractal per log (z)


Puoi usare nomi più corti (1 carattere) e rimuovere gli spazi bianchi combinando più linee usando ;. Inoltre, rimuovere tutti gli spazi possibili.
mbomb007,

Alcuni golf regolari riducono questo a soli 353 byte ! Non l'ho provato (no matplotlibqui), quindi nessuna garanzia che funzioni ancora.
Khuldraeseth na'Barya,
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.