Disegna una guarnizione apolloniana


28

Dati tre cerchi reciprocamente tangenti, possiamo sempre trovare altri due cerchi tangenti a tutti e tre questi. Questi due sono chiamati cerchi apolloniani . Si noti che uno dei cerchi apolloniani potrebbe effettivamente essere attorno ai tre cerchi iniziali.

A partire da tre cerchi tangenti, possiamo creare un frattale chiamato guarnizione apolloniana , con il seguente processo:

  1. Chiama i 3 cerchi iniziali come i cerchi principali
  2. Trova i due cerchi apolloniani delle cerchie principali
  3. Per ogni circolo apolloniano:
    1. Per ogni coppia delle tre coppie di cerchi principali:
      1. Chiama la cerchia apolloniana e le due cerchie genitore la nuova serie di cerchie genitore e ricomincia dal passaggio 2.

Ad esempio, iniziando con cerchi di uguali dimensioni, otteniamo:

inserisci qui la descrizione dell'immagine

Immagine trovata su Wikipedia

C'è ancora un po 'di notazione di cui abbiamo bisogno. Se abbiamo un cerchio di raggio r con centro (x, y) , possiamo definirne la curvatura come k = ± 1 / r . Di solito k sarà positivo, ma possiamo usare k negativo per indicare il cerchio che racchiude tutti gli altri cerchi nella guarnizione (cioè tutte le tangenti toccano quel cerchio dall'interno). Quindi possiamo specificare un cerchio con una tripletta di numeri: (k, x * k, y * k) .

Ai fini della presente domanda, si suppone intero positivo k e razionali x ed y .

Ulteriori esempi per tali cerchie sono disponibili nell'articolo di Wikipedia .

Ci sono anche alcune cose interessanti sulle guarnizioni integrali in questo articolo (tra le altre cose divertenti con le cerchie).

La sfida

Ti verranno fornite 4 specifiche del cerchio, ognuna delle quali apparirà (14, 28/35, -112/105). È possibile utilizzare qualsiasi formato elenco e operatore di divisione che sia conveniente, in modo tale da poter semplicemente evalinserire l'input, se lo si desidera. Si può presumere che i 4 cerchi siano effettivamente tangenti tra loro e che il primo di essi abbia una curvatura negativa. Ciò significa che ti è già stato dato il circolo apolloniano circostante degli altri tre. Per un elenco di input di esempio validi, vedere il fondo della sfida.

Scrivi un programma o una funzione che, dato questo input, disegna una guarnizione apolloniana.

Puoi inserire input tramite argomento di funzione, ARGV o STDIN e visualizzare il frattale sullo schermo o scriverlo in un file di immagine in un formato a tua scelta.

Se l'immagine risultante viene rasterizzata, deve essere di almeno 400 pixel su ciascun lato, con un'imbottitura inferiore al 20% attorno al cerchio più grande. È possibile interrompere la ricorrenza quando si raggiungono cerchi il cui raggio è inferiore a un 400 ° del cerchio di input più grande o cerchi più piccoli di un pixel, a seconda di quale evento si verifichi per primo.

Devi disegnare solo contorni circolari, non dischi completi, ma i colori di sfondo e linee sono la tua scelta. I contorni non devono essere più larghi di un 200 ° del diametro dei cerchi esterni.

Questo è il golf del codice, quindi vince la risposta più breve (in byte).

Ingressi di esempio

Ecco tutte le guarnizioni integrali dell'articolo di Wikipedia convertite nel formato di input prescritto:

[[-1, 0, 0], [2, 1, 0], [2, -1, 0], [3, 0, 2]]
[[-2, 0, 0], [3, 1/2, 0], [6, -2, 0], [7, -3/2, 2]]
[[-3, 0, 0], [4, 1/3, 0], [12, -3, 0], [13, -8/3, 2]]
[[-3, 0, 0], [5, 2/3, 0], [8, -4/3, -1], [8, -4/3, 1]]
[[-4, 0, 0], [5, 1/4, 0], [20, -4, 0], [21, -15/4, 2]]
[[-4, 0, 0], [8, 1, 0], [9, -3/4, -1], [9, -3/4, 1]]
[[-5, 0, 0], [6, 1/5, 0], [30, -5, 0], [31, -24/5, 2]]
[[-5, 0, 0], [7, 2/5, 0], [18, -12/5, -1], [18, -12/5, 1]]
[[-6, 0, 0], [7, 1/6, 0], [42, -6, 0], [43, -35/6, 2]]
[[-6, 0, 0], [10, 2/3, 0], [15, -3/2, 0], [19, -5/6, 2]]
[[-6, 0, 0], [11, 5/6, 0], [14, -16/15, -4/5], [15, -9/10, 6/5]]
[[-7, 0, 0], [8, 1/7, 0], [56, -7, 0], [57, -48/7, 2]]
[[-7, 0, 0], [9, 2/7, 0], [32, -24/7, -1], [32, -24/7, 1]]
[[-7, 0, 0], [12, 5/7, 0], [17, -48/35, -2/5], [20, -33/35, 8/5]]
[[-8, 0, 0], [9, 1/8, 0], [72, -8, 0], [73, -63/8, 2]]
[[-8, 0, 0], [12, 1/2, 0], [25, -15/8, -1], [25, -15/8, 1]]
[[-8, 0, 0], [13, 5/8, 0], [21, -63/40, -2/5], [24, -6/5, 8/5]]
[[-9, 0, 0], [10, 1/9, 0], [90, -9, 0], [91, -80/9, 2]]
[[-9, 0, 0], [11, 2/9, 0], [50, -40/9, -1], [50, -40/9, 1]]
[[-9, 0, 0], [14, 5/9, 0], [26, -77/45, -4/5], [27, -8/5, 6/5]]
[[-9, 0, 0], [18, 1, 0], [19, -8/9, -2/3], [22, -5/9, 4/3]]
[[-10, 0, 0], [11, 1/10, 0], [110, -10, 0], [111, -99/10, 2]]
[[-10, 0, 0], [14, 2/5, 0], [35, -5/2, 0], [39, -21/10, 2]]
[[-10, 0, 0], [18, 4/5, 0], [23, -6/5, -1/2], [27, -4/5, 3/2]]
[[-11, 0, 0], [12, 1/11, 0], [132, -11, 0], [133, -120/11, 2]]
[[-11, 0, 0], [13, 2/11, 0], [72, -60/11, -1], [72, -60/11, 1]]
[[-11, 0, 0], [16, 5/11, 0], [36, -117/55, -4/5], [37, -112/55, 6/5]]
[[-11, 0, 0], [21, 10/11, 0], [24, -56/55, -3/5], [28, -36/55, 7/5]]
[[-12, 0, 0], [13, 1/12, 0], [156, -12, 0], [157, -143/12, 2]]
[[-12, 0, 0], [16, 1/3, 0], [49, -35/12, -1], [49, -35/12, 1]]
[[-12, 0, 0], [17, 5/12, 0], [41, -143/60, -2/5], [44, -32/15, 8/5]]
[[-12, 0, 0], [21, 3/4, 0], [28, -4/3, 0], [37, -7/12, 2]]
[[-12, 0, 0], [21, 3/4, 0], [29, -5/4, -2/3], [32, -1, 4/3]]
[[-12, 0, 0], [25, 13/12, 0], [25, -119/156, -10/13], [28, -20/39, 16/13]]
[[-13, 0, 0], [14, 1/13, 0], [182, -13, 0], [183, -168/13, 2]]
[[-13, 0, 0], [15, 2/13, 0], [98, -84/13, -1], [98, -84/13, 1]]
[[-13, 0, 0], [18, 5/13, 0], [47, -168/65, -2/5], [50, -153/65, 8/5]]
[[-13, 0, 0], [23, 10/13, 0], [30, -84/65, -1/5], [38, -44/65, 9/5]]
[[-14, 0, 0], [15, 1/14, 0], [210, -14, 0], [211, -195/14, 2]]
[[-14, 0, 0], [18, 2/7, 0], [63, -7/2, 0], [67, -45/14, 2]]
[[-14, 0, 0], [19, 5/14, 0], [54, -96/35, -4/5], [55, -187/70, 6/5]]
[[-14, 0, 0], [22, 4/7, 0], [39, -12/7, -1/2], [43, -10/7, 3/2]]
[[-14, 0, 0], [27, 13/14, 0], [31, -171/182, -10/13], [34, -66/91, 16/13]]
[[-15, 0, 0], [16, 1/15, 0], [240, -15, 0], [241, -224/15, 2]]
[[-15, 0, 0], [17, 2/15, 0], [128, -112/15, -1], [128, -112/15, 1]]
[[-15, 0, 0], [24, 3/5, 0], [40, -5/3, 0], [49, -16/15, 2]]
[[-15, 0, 0], [24, 3/5, 0], [41, -8/5, -2/3], [44, -7/5, 4/3]]
[[-15, 0, 0], [28, 13/15, 0], [33, -72/65, -6/13], [40, -25/39, 20/13]]
[[-15, 0, 0], [32, 17/15, 0], [32, -161/255, -16/17], [33, -48/85, 18/17]]

La tua illustrazione di esempio sembra includere solo i circoli apolloniani "interni" dopo la prima operazione.
Sparr,

@Sparr Non sono sicuro di cosa intendi. Dopo la prima operazione, esiste già uno dei due cerchi apolloniani (il cerchio genitore originale che non hai scelto per l'iterazione corrente) e stai solo cercando l'altra soluzione.
Martin Ender,

Poco male, hai ragione, stavo leggendo male.
Sparr

Risposte:


12

GolfScript (289 byte vettoriali / 237 byte raster)

A 289 byte e l'esecuzione in un tempo ragionevole:

'/'/n*','/']['*0,`1/*~1.$[]*(~-400*:&;{1+1=*}/:D;{{1+2<~D@*\/}%}%'<svg><g fill="none" stroke="red">'puts.{[[~@:b[D&*\abs]{@&*[b]+}2*]{'.0/'*'"#{
}"'n/*~}%'<circle r="
" cx="
" cy="
" />'n/\]zip puts}:|/[{.([.;]+}3*]{(:?zip{)\~++2*\-}%:c.|0=D&*<{?);[c]+[{([.;]+.}3*;]+}*.}do'</g></svg>'

Questo prende input su stdin e genera un file SVG su stdout. Purtroppo ci vuole un po 'troppo tempo per una demo online, ma una versione ottimizzata che si interrompe presto può darti un'idea.

Dato input, [[-2, 0, 0], [3, 1/2, 0], [6, -2, 0], [7, -3/2, 2]]l' output (convertito in PNG con InkScape) è

guarnizione 2/3/6/7


A 237 byte e impiegando troppo tempo (estrapolo che ci vorrebbe poco più di una settimana per produrre un output simile a quello sopra, anche se in bianco e nero a un bit):

'/'/n*','/']['*0,`1/*~1.$[]*(~-400*:&;{1+1=*}/:D;{{1+2<~D@*\/}%}%.[{.([.;]+}3*]{(:?[zip{)\~++2*\-}%:c]@+\0c=D&*<{?);[c]+[{([.;]+.}3*;]+}*.}do;:C;'P1 ''801 '2*.~:B*,{:P;C{:?[0=2/.D&*-.*\D&*+.*]{2,{P{B/}2$*B%400-?0=*\)?=&*-.*}/+<},,1=},!}/

L'output è in formato NetPBM senza newline, quindi probabilmente non segue rigorosamente le specifiche, anche se GIMP lo caricherà comunque. Se è richiesta una conformità rigorosa, inserire un ndopo l'ultimo !.

La rasterizzazione consiste nel testare ciascun pixel rispetto a ciascun cerchio, quindi il tempo impiegato è praticamente lineare nel numero di pixel per il numero di cerchi. Ridimensionando tutto di un fattore 10,

'/'/n*','/']['*0,`1/*~1.$[]*(~-40*:&;{1+1=*}/:D;{{1+2<~D@*\/}%}%.[{.([.;]+}3*]{(:?[zip{)\~++2*\-}%:c]@+\0c=D&*<{?);[c]+[{([.;]+.}3*;]+}*.}do;:C;'P1 ''81 '2*.~:B*,{:P;C{:?[0=2/.D&*-.*\D&*+.*]{2,{P{B/}2$*B%40-?0=*\)?=&*-.*}/+<},,1=},!}/

durerà tra 10 minuti e produrrà

Immagine 81x81

(convertito in PNG con GIMP). Dato 36 ore ha prodotto il 401x401

Immagine 401x401


3
Non avrei mai pensato di poter fare un output grafico con Golfscript ...
Decadimento beta

12

JavaScript ( 418 410 byte)

Implementato come funzione:

function A(s){P='<svg><g fill=none stroke=red transform=translate(400,400)>';Q=[];s=eval(s);S=-400*s[0][0];function d(c){P+='<circle r='+Math.abs(p=S/c[0])+' cx='+p*c[1]+' cy='+p*c[2]+' />'}for(c=4;c--;d(s[0]),s.push(s.shift()))Q.push(s.slice());for(;s=Q.shift();d(c)){c=[];for(i=4;i--;)c[i]=2*(s[0][i]+s[1][i]+s[2][i])-s[3][i];for(i=6;c[0]<S&&i;)Q.push([s[i--%3],s[i--%3],c,s[i%3]])}document.body.innerHTML=P}

Demo online (nota: non funziona nei browser che non rispettano i requisiti delle specifiche SVG rispetto al dimensionamento implicito, quindi offro una versione leggermente più lunga che aggira quel bug; i browser possono anche rendere lo SVG meno accurato rispetto ad esempio a Inkscape, sebbene Inkscape sia un po 'più rigoroso nel citare gli attributi).

Si noti che è possibile salvare 8 byte utilizzando document.write, ma ciò ostacola seriamente jsFiddle.


1
Probabilmente puoi risparmiare di più definendo la funzione con ES6 e memorizzando, ad esempio, S/c[0]in una variabile e poi anche liberarti di Math.abscon un operatore ternario ecc.
Ingo Bürk

@ IngoBürk, se avessi intenzione di seguire il percorso ES6, lo scriverei invece in CoffeeScript.
Peter Taylor,

usa l'host c99.nl. Permette document.write.
xem,

2
È bello vedere una risposta a questo :)
MickyT

Aggiornato con il suggerimento di @ IngoBürk per una variabile temporanea. Eliminare Math.abscosterebbe davvero un personaggio.
Peter Taylor,

6

Mathematica 289 caratteri

Risolvendo il sistema bilineare secondo http://arxiv.org/pdf/math/0101066v1.pdf Teorema 2.2 (altamente inefficiente).

Spazi non necessari, ancora giocando a golf:

w = {k, x, y};
d = IdentityMatrix;
j = Join;
p_~f~h_ := If[#[[-1, 1]] < 6! h,
    q = 2 d@4 - 1;
    m = #~j~{w};
    r = Complement[w /. NSolve[ And @@ j @@ 
                        MapThread[Equal, {Thread@m.q.m, 4 d@3 {0, 1, 1}}, 2], w], a];
    If[r != {},
     a~AppendTo~# & @@ r;
     Function[x, x~j~{#}~f~h & /@ r]@#]] & /@ p~Subsets~{3}; 
Graphics[Circle @@@ ({{##2}, 1}/# & @@@ (f[a = #, -Tr@#]; a))] &

Un'animazione di dimensioni ridotte con input {{-13, 0, 0}, {23, 10/13, 0}, {30, -84/65, -1/5}, {38, -44/65, 9/5}}

inserisci qui la descrizione dell'immagine


Come prendi l'input?
Martin Ender,

@ MartinBüttner come argomento di funzione, aggiungendo @{{-1, 0, 0}, {2, 1, 0}, {2, -1, 0}, {3, 0, 2}}all'ultima riga
Dr. belisarius,

@ MartinBüttner Se hai intenzione di testarlo prova prima con 50/hinvece di 400/h. Otterrai il risultato più velocemente. inoltre, è possibile monitorare i progressi entrando Dynamic@Length@aprima di eseguire la funzione
Dr. belisarius,

Instructions for testing this answer (with a reduced number of circles) without Mathematica installed: 1) Scarica questo da pastebin e salvalo come * .CDF 2) Scarica e installa l'ambiente CDF gratuito da Wolfram Research all'indirizzo (non un piccolo file). Godere. Dimmi se funziona! - Nota: i calc sono lenti, attendi che la grafica venga visualizzata.
Dr. belisarius,

A cosa si riferisce il commento "altamente inefficiente"? È che (guardando l'animazione) apparentemente stai disegnando la maggior parte dei cerchi almeno due volte? Penso che il complesso approccio di Cartesio sia intrinsecamente efficiente come si arriva.
Peter Taylor,

4

Acero (960 byte)

Ho usato il Teorema di Cartesio per generare la guarnizione apolloniana e quindi usare il sistema di tracciamento di Maple per tracciarlo. Se ho tempo, voglio continuare a giocare a golf e cambiarlo in Python (Maple non è sicuramente il migliore per i frattali). Ecco un link ad un lettore Maple gratuito se vuoi eseguire il mio codice.

X,Y,Z,S,N:=abs,evalf,member,sqrt,numelems;
f:=proc(J)
    L:=map((x)->[x[1],(x[2]+x[3]*I)/x[1]+50*(1+I)/X(J[1][2])],J);
    R:=Vector([L]);
    T,r:=X(L[1][3]),L[1][4];
    A(L[1][5],L[2][6],L[3][7],L[1][8],L[2][9],L[3][10],R,T,r);
    A(L[1][11],L[2][12],L[4][13],L[1][14],L[2][15],L[4][16],R,T,r);
    A(L[1][17],L[3][18],L[4][19],L[1][20],L[3][21],L[4][22],R,T,r);
    A(L[2][23],L[3][24],L[4][25],L[2][26],L[3][27],L[4][28],R,T,r);
    plots[display](seq(plottools[circle]([Re(R[i][29]),Im(R[i][30])],X(1/R[i][31])),i=1..N(R))):
end proc:
A:=proc(a,b,c,i,j,k,R,E,F)
    K:=i+k+j+2*S(i*k+i*j+k*j);
    if K>400*E then
    return;
    end if;
    C:=(a*i+c*k+b*j+2*S(a*c*i*k+b*c*j*k+a*b*i*j))/K;
    C2:=(a*i+c*k+b*j-2*S(a*c*i*k+b*c*j*k+a*b*i*j))/K;
    if Y(X(C-F))<1/E and not Z([K,C],R) then
    R(N(R)+1):=[K,C];
    A(a,b,C,i,j,K,R,E,F);
    A(a,c,C,i,k,K,R,E,F);
    A(b,c,C,j,k,K,R,E,F);
    end if:    
    if Y(X(C2-F))<1/E and not Z([K,C2],R) then
    R(N(R)+1):=[K,C2];
    A(a,b,C2,i,j,K,R,E,F);
    A(a,c,C2,i,k,K,R,E,F);
    A(b,c,C2,j,k,K,R,E,F);
    end if: 
end proc:

Alcune guarnizioni di esempio

f([[-1, 0, 0], [2, 1, 0], [2, -1, 0], [3, 0, 2]]);

inserisci qui la descrizione dell'immagine

f([[-9, 0, 0], [14, 5/9, 0], [26, -77/45, -4/5], [27, -8/5, 6/5]]);

inserisci qui la descrizione dell'immagine

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.