Algebraischer Kurvenplotter

14

Eine algebraische Kurve ist eine bestimmte "1D-Teilmenge" der "2D-Ebene", die als Nullmenge {(x,y) in R^2 : f(x,y)=0 }eines Polynoms beschrieben werden kann f. Hier betrachten wir die 2D-Ebene als die reale Ebene, R^2so dass wir uns leicht vorstellen können, wie eine solche Kurve aussehen könnte, im Grunde eine Sache, die Sie mit einem Bleistift zeichnen können.

Beispiele:

  • 0 = x^2 + y^2 -1 ein Kreis mit Radius 1
  • 0 = x^2 + 2y^2 -1 eine Ellipse
  • 0 = xy eine Kreuzform, im Allgemeinen der Vereinigung der x-Achse und die y-Achse
  • 0 = y^2 - x eine Parabel
  • 0 = y^2 - (x^3 - x + 1)eine elliptische Kurve
  • 0 = x^3 + y^3 - 3xy das Folium von Descartes
  • 0 = x^4 - (x^2 - y^2) eine Lemniskate
  • 0 = (x^2 + y^2)^2 - (x^3 - 3xy^2) ein Trifolium
  • 0 = (x^2 + y^2 - 1)^3 + 27x^2y^2 ein Astroid

Aufgabe

Ausgehend von einem Polynom f(wie unten definiert) und den x / y-Bereichen wird ein Schwarzweißbild mit mindestens 100 x 100 Pixeln ausgegeben, das die Kurve als schwarze Linie auf weißem Hintergrund darstellt.

Einzelheiten

Farbe : Sie können zwei beliebige andere Farben Ihrer Wahl verwenden. Es sollte einfach sein, sie voneinander zu unterscheiden.

Plot : Anstelle eines Pixelbildes können Sie dieses Bild auch als ASCII-Grafik ausgeben, wobei der Hintergrund "Pixel" ein Leerzeichen / Unterstrich oder ein anderes Zeichen sein sollte, das "leer aussieht", und die Linie kann aus einem Zeichen bestehen, das aussieht. " voll "wie Moder Xoder #.

Sie müssen sich nicht um Aliasing kümmern.

Sie nur zu Plotlinien müssen , wo das Vorzeichen der Polynom ändert sich von einer Seite der Linie zum anderen (das heißt Sie zB den Marsch - Square - Algorithmus verwenden könnte), die Sie nicht richtig geplottet müssen „pathologische Fälle wie , 0 = x^2wo das Zeichen tut ändert sich nicht , wenn man von einer Seite der Linie zur anderen geht, aber die Linie sollte durchgehend sein und die Bereiche der verschiedenen Zeichen von f(x,y).

Polynom : Das Polynom wird als (m+1) x (n+1)Matrix / Liste von Listen von (reellen) Koeffizienten angegeben. Im folgenden Beispiel werden die Begriffe der Koeffizienten in ihrer Position angegeben:

[   1 * 1,   1 * x,   1 * x^2,   1 * x^3,  ... , 1 * x^n ]
[   y * 1,   y * x,   y * x^2,   y * x^4,  ... , y * x^n ]
[   ...  ,   ...   ,   ...   ,    ...   ,  ... ,   ...   ]
[ y^m * 1, y^m * x, y^m * x^2, y^m * x^3 , ..., y^m * x^n]

Wenn Sie möchten, können Sie davon ausgehen, dass die Matrix quadratisch ist (dies kann immer mit dem erforderlichen Nullabstand erfolgen), und wenn Sie möchten, können Sie auch davon ausgehen, dass die Größe der Matrix als zusätzliche Eingabe angegeben wird.

Die obigen Beispiele werden im Folgenden als eine wie folgt definierte Matrix dargestellt:

Circle:       Ellipse:      Parabola:  Cross:    Elliptic Curve: e.t.c
[-1, 0, 1]    [-1, 0, 1]    [ 0,-1]    [ 0, 0]   [-1, 1, 0,-1]
[ 0, 0, 0]    [ 0, 0, 0]    [ 0, 0]    [ 0, 1]   [ 0, 0, 0, 0]
[ 1, 0, 0]    [ 2, 0, 0]    [ 1, 0]              [ 1, 0, 0, 0]

Testfälle mit x-Bereich / y-Bereich:

(In einem nicht so lesbaren, aber besser kopierbaren Format, das hier im Pastebin verfügbar ist .)

Circle:     
[-1, 0, 1]   [-2,2]   [-2,2]
[ 0, 0, 0]
[ 1, 0, 0]

Ellipse:
[-1, 0, 1]   [-2,2]   [-1,1]
[ 0, 0, 0]
[ 2, 0, 0]

Cross:
[ 0, 0]      [-1,2]   [-2,1]
[ 0, 1]

Parabola:
[ 0,-1]      [-1,3]   [-2,2]
[ 0, 0]
[ 1, 0]

Elliptic Curve:
[-1, 1, 0,-1]    [-2,2]   [-3,3]
[ 0, 0, 0, 0]  
[ 1, 0, 0, 0]  

Folium of Descartes:
[  0,  0,  0,  1]    [-3,3]   [-3,3]
[  0, -3,  0,  0]
[  0,  0,  0,  0]
[  1,  0,  0,  0]

Lemniscate:
[  0,  0, -1,  0,  1]    [-2,2]   [-1,1]
[  0,  0,  0,  0,  0]
[  1,  0,  0,  0,  0]

Trifolium:
[ 0, 0, 0,-1, 1]    [-1,1]   [-1,1]
[ 0, 0, 0, 0, 0]
[ 0, 3, 2, 0, 0]
[ 0, 0, 0, 0, 0]
[ 1, 0, 0, 0, 0]

Astroid:
[ -1,  0,  3,  0, -3,  0,  1]    [-1,1]   [-1,1]
[  0,  0,  0,  0,  0,  0,  0]
[  3,  0, 21,  0,  3,  0,  0]
[  0,  0,  0,  0,  0,  0,  0]
[ -3,  0,  3,  0,  0,  0,  0]
[  0,  0,  0,  0,  0,  0,  0]
[  1,  0,  0,  0,  0,  0,  0]

Ich habe die Inspiration für einige Kurven aus diesem PDF.

fehlerhaft
quelle
Bedeutet " Sie müssen sich nicht um Aliasing kümmern ", dass wir jedes Pixel nur danach einfärben können, ob sein Mittelpunkt auf der Linie liegt?
Peter Taylor
Ich sehe keine Verbindung zum Aliasing. Aber nein, es sollte eine durchgehende Linie zwischen den Regionen mit unterschiedlichen Vorzeichen geben.
Fehler
Die Matrix ist nicht mx n, sondern (m+1)x (n+1). Was nehmen wir als Eingabe: m, noder m+1,n+1? Oder können wir wählen?
Luis Mendo
Können wir die grafische Funktion nur in einem neuen Fenster anzeigen?
R. Kap
1
@ LuisMendo Ja, die Achse kann in jede beliebige Richtung verlaufen. (Solange sie orthogonal sind =)
Fehler

Antworten:

10

Haskell, 283 275 Bytes

Die Funktion gsollte mit der Matrix und den beiden Bereichen als Argumente aufgerufen werden. Die Matrix ist nur eine Liste von Listen, die Bereiche jeweils eine Liste mit zwei Elementen.

import Data.List
t=transpose
u=tail
z=zipWith
l%x=sum$z(*)l$iterate(*x)1                                   --generate powers and multiply with coefficients
e m y x=[l%x|l<-m]%y                                         --evaluate the encoded polynomial
a#b=[a,a+(b-a)/102..b]                                       --create a range
g m[u,w][i,j]=unlines$v[map((0<).e m y)$u#w|y<-i#j]          --evaluate the function on the grid, get the sign
f g=u[u$u$map fst$scanl(\(r,l)c->(c==l,c))(1<0,1<0) l|l<-g]  --find +- or -+ transitions within lines
a&b|a&&b=' '|0<1='#'                                         --helper function for creating the string
v g=z(z(&))(f g)(t$f$t g)                                    --create the string

Hier die Ausgaben für die interessanteren Fälle: Beachten Sie, dass ich das Ergebnis von 100x100 auf ungefähr 40x40 verkleinern musste, damit es in die Konsole passt (ändern Sie einfach die fest codierte 102 in eine kleinere Zahl). Beachten Sie auch, dass die y-Achse nach unten zeigt.

fehlerhaft
quelle
Es gibt ein paar hübsche kleine Golfplätze, die Sie hier machen können. In der letzten Zeile werden Parens verwendet $, um ein Byte zu speichern. Beide Orte, an denen Sie sich befinden mapkönnten (<$>), und da Sie nur eeinmal verwenden, können Sie das (0<)Innere ziehen, um es zu definieren. Auch ekönnte benannt werden (!), um 3 Bytes zu sparen.
Post Rock Garf Hunter
Und das Einfügen zin die Definition von vermöglicht es Ihnen, 4 Klammern (um z(&)und f g) loszuwerden .
Post Rock Garf Hunter
Sie können auch #ein einzelnes Zeichen umbenennen (z. B. s) und stattdessen eine Musterübereinstimmung in den Listen vornehmen g. (zB s[a,b]=[a,a+(b-a)/102..b];g m u i=unlines$v[m!y<$>s u|y<-s i])
Post Rock Garf Hunter
6

Matlab, 114 100 92 Bytes

Das richtige Werkzeug für den Job? Ich benutze die interessante Methode von Matlab printf, um ein Polynom als Zeichenfolge zu generieren. Dieses Polynom kann angegeben werden, für ezplotdas die implizite Kurve in der angegebenen Domäne gezeichnet wird. Zur besseren Lesbarkeit wird der Code nachher mit Zeilenumbrüchen dargestellt; was nicht benötigt wird und nicht auf die Größe angerechnet wird.

function P(A,W,H,h,w)
t=0:h*w-1;
ezplot(sprintf('+%d*x^%.0f*y^%d',[A(:)';t/h;rem(t,h)]),[W,H])

Golf-Fortschritt als erweiterbares Snippet.


Ausgabe der Testfälle (zum Vergrößern anklicken): Testfälle

Algmyr
quelle
2
Wirklich schöne Lösung mit sprintf/ezplot!
Fehler
Verwenden von fixstatt floorkönnte Ihnen helfen, die zweistellige Byteanzahl zu erreichen :-)
Luis Mendo
Sie können auch [h,w]=size(A);t=0:h*w-1;drei weitere Bytes speichern!
Fehler
@ LuisMendo Eigentlich kann ich es besser machen. Ich war traurig, dass Matlabs printf keinen ganzzahligen Platzhalter hat, aber dennoch Dinge wie unterstützt %.0f. Das heißt, ich kann den Boden ganz fallen lassen und printfreparieren lassen!
Algmyr
@flawr Ich benutze den zweiten Teil davon in späteren Iterationen. Mir ist klar, dass meine Formatierung mit der besten letzten Version nicht ganz klar war. Die Formatierung wurde bearbeitet, um dies kristallklar zu machen.
Algmyr
6

Python 2, 261 Bytes

E=enumerate
M,[a,c],[b,d]=input()
e=(c-a)/199.
I=200
J=-int((b-d)/e-1)
print'P2',I,J,255
i=I*J
while i:i-=1;x,y=c-i%I*e,b+i/I*e;u,v,w=map(sum,zip(*((z*p/x,z*q/y,z)for q,R in E(M)for p,t in E(R)for z in[t*x**p*y**q])));print int(255*min(1,(w*w/(u*u+v*v))**.5/e))

Eingabeformat: matrix,xbounds,ybounds(zB [[-1,0,1],[0,0,0],[1,0,0]],[-2,2],[-2,2]). Ausgabeformat: normales PGM .

Dies schätzt den Abstand von jedem Pixelzentrum zur Kurve unter Verwendung der Näherung erster Ordnung d ( x , y ) = | p ( x , y ) | / | ∇ p ( x , y ) |, wobei ∇ p der Gradient des Polynoms p ist . (Dies ist der Abstand von ( x , y ) zum Schnittpunkt der Tangentialebene bei ( x , y , p ( x , y )) mit der xy- Ebene.) Dann sind die Pixel mit d (x , y ) ist proportional zu d ( x , y ) kleiner als eine Pixelbreite der Kurve , was zu schönen Antialias-Linien führt (auch wenn dies nicht erforderlich ist).

Ausgabe

Hier sind die gleichen Grafiken mit der Distanzfunktion geteilt durch 16 , um sie sichtbar zu machen.

Anders Kaseorg
quelle
Warten Sie, wo im Code findet das eigentliche grafische Plotten statt?
R. Kap
@ R.Kap Der Code schreibt ein Bild im normalen PGM-Format nach stdout. Es gibt eine printAnweisung für den Bildkopf und eineprint Anweisung in der whileSchleife für den Wert jedes Pixels.
Anders Kaseorg
Wow, das ist echt cool! Würde es Ihnen etwas ausmachen, Ihren Plot-Algorithmus etwas genauer zu betrachten?
Fehler
@flawr Ich habe die Erklärung ein bisschen erweitert; Beantwortet das Ihre Fragen?
Anders Kaseorg
@AndersKaseorg Ja, vielen Dank!
Fehler
5

Python 3.5 + MatPlotLib + Numpy, 352 Byte:

from matplotlib.pyplot import*;from numpy import*
def R(M,S,U,r=range):N=linspace;E='+'.join([str(y)+'*'+m for y,m in[q for i,g in zip(M,[[i+'*'+p for p in['1']+['x^%d'%p for p in r(1,len(M[0]))]]for i in['1']+['y^%d'%i for i in r(1,len(M))]])for q in zip(i,g)if q[0]]]);x,y=meshgrid(N(*S,200),N(*U,200));contour(x,y,eval(E.replace('^','**')),0);show()

Eine benannte Funktion. Ziemlich lange, aber hey, ich bin nur froh, dass ich die Aufgabe erfüllen konnte. Nimmt 3 Eingaben an, die die m by nMatrix, den xBereich und den yBereich sind, die alle in Arrays sein sollen (zum Beispiel [[-1,0,1],[0,0,0],[1,0,0]],[-2,2],[-2,2]). Gibt das fertige Diagramm in einem neuen, grafischen, interaktiven Fenster aus. Wird das Golfspielen länger dauern, wenn ich kann, aber im Moment bin ich damit zufrieden.

Endgültige Ausgaben für die Testfälle:

Endgültige Ausgabe

R. Kap
quelle
5

MATL , 67 61 Bytes

8Wt:qwq/t2:"wid*2M1)+i:q!^]!2&!w[1IK2]&!**ss&eZS5Y62&Y+|4=0YG

Dieser Code wird in Version 18.5.0 der Sprache ausgeführt, die der Herausforderung vorausgeht. Input verwendet die optional m, nParameter. Die Matrix enthält Semikolons als Zeilentrennzeichen. Das genaue Eingabeformat (am Beispiel der Parabel) ist

[-1,3]
3  
[-2,2]
2
[0,-1; 0, 0; 1, 0]

Der Code erzeugt ein Bild mit der Größe 255 × 255. Dies kann mit dem MATL Online- Compiler von @Suever getestet werden , der unter anderem eine grafische Ausgabe enthält. Siehe zum Beispiel

Dieser Compiler befindet sich noch in einem experimentellen Stadium. Bitte melden Sie Probleme an @Suever im MATL-Chatroom . Wenn die Schaltfläche "Ausführen" nicht funktioniert, aktualisieren Sie die Seite und klicken Sie erneut.

Wenn Sie die ASCII-Ausgabe bevorzugen , muss der Code ein wenig geändert werden (die Änderungen betreffen nur die ersten beiden und letzten vier Zeichen des obigen Codes):

101t:qwq/t2:"wid*2M1)+i:q!^]!2&!w[1IK2]&!**ss&eZS5Y62&Y+|4<42*c

Dies erzeugt ein 100 × 100-ASCII-Gitter, das Zeichen *zur Darstellung der Kurve verwendet. Sie können dies auch mit @Dennis ' online testen !Plattform:

Beachten Sie, dass das Seitenverhältnis der ASCII-Ausgabe geändert wird, da die Zeichen etwas höher als breit sind.

Erläuterung

Der Code berechnet zuerst das Polynom mit zwei Variablen in einem x - y- Gitter. Dabei wird das Broadcasting intensiv genutzt und ein 4D-Zwischenarray berechnet, bei dem jede Dimension x- Werte, y- Werte, x- Exponenten und y darstellt Exponenten darstellt.

Aus dieser Funktion wird die Nulllinie berechnet. Da die Abfrage angibt, dass nur Vorzeichenänderungen erkannt werden müssen, wendet der Code eine 2D-Faltung mit einem 2 × 2-Block von Einsen an und markiert ein Pixel als zur Linie gehörend, wenn nicht die vier Werte des Blocks dasselbe Vorzeichen haben.

8W      % Push 2^8, that is, 256. (The ASCII-output version pushes 101 instead)
t:q     % Duplicate. Push range [0 1 ... 255]
wq      % Swap. Subtract 1 to obtain 255
/       % Divide. Gives normalized range [0 1/255 2/255... 1]
t       % Duplicate
2:"     % For loop: do this twice
  w     %   Swap top two elements in the stack
  i     %   Input two-number array defining x range (resp. y in second iteration)
  d     %   Difference of the two entries
  *     %   Multiply by normalized range
  2M1)  %   Push the array again and get its first entry
  +     %   Add. This gives the range for x values (resp. y)
  i     %   Input m (n in second iteration)
  :q    %   Range [0 1 ...m-1] (resp. [0 1 ...n-1])
  !     %   Convert to column array
  ^     %   Power, element-wise with broadcast. This gives a matrix of size m×256
        %   (resp. n×256) of powers of x (resp. y) for the range of values computed
        %   previously
]       % End for loop
!       % Transpose. This transforms the n×256 matrix of powers of y into 256×n
2       % Push 2
&!      % Permute dimensions 1 and 3: transforms the 256×n matrix into a 4D array
        % of size 1×n×256×1
w       % Swap top two elements in the stack: bring 256×m matrix to top
[1IK2]  % Push vector [1 3 4 2]
&!      % Permute dimensions as indicated by the vector: transforms the m×256 matrix
        % into a 4D array of size m×1×1×256
*       % Multiply element-wise with broadcast: gives 4D array of size m×n×256×256
        % with mixed powers of x and y for at the grid of x, y values
*       % Implicitly input m×n matrix. Multiply element-wise with broadcast: gives
        % 4D array of size m×n×256×256
ss      % Sum along first two dimensions: gives 4D array of size 1×1×256×256
&e      % Squeeze singleton dimensions: gives matrix of size 256×256. This is the
        % two-variable polynomial evaluated at the x, y grid.
        % Now we need to find the zero level curve of this function. We do this by 
        % detecting when the sign of the function changes along any of the two axes
ZS      % Matrix of sign values (1, 0 or -1)
5Y6     % Predefined literal: matrix [1 1; 1 1]
2&Y+    % Compute 2D convolution, keeping only the valid (central) part
|4=     % True if absolute value of result is 4, which indicates no sign changes.
        % (The ASCII version computes a negated version of this, for better display)
0YG     % Display as image. (The ASCII-output version does the following instead:
        % multiply by 42 and convert to char. 42 is ASCII for '*', and character 0 
        % is shown as space. The 2D char array is then implicitly displayed)

Alle Testfälle

Hier sind alle Eingaben im entsprechenden Format, falls Sie es versuchen möchten:

Circle:
[-2,2]
3
[-2,2]
3
[-1, 0, 1; 0, 0, 0; 1, 0, 0]

Ellipse:
[-2,2]
3
[-1,1]
3
[-1, 0, 1; 0, 0, 0; 2, 0, 0]

Cross:
[-1,2]
2
[-2,1]
2
[0, 0; 0, 1]

Parabola:
[-1,3]
3  
[-2,2]
2
[0,-1; 0, 0; 1, 0]

Elliptic Curve:
[-2,2]
3
[-3,3]
4
[-1, 1, 0,-1; 0, 0, 0, 0; 1, 0, 0, 0]

Folium of Descartes:
[-3,3]
4
[-3,3]
4
[0,  0,  0,  1; 0, -3,  0,  0; 0,  0,  0,  0; 1,  0,  0,  0]


Lemniscate:
[-2,2]
3
[-1,1]
5
[0,  0, -1,  0,  1; 0,  0,  0,  0,  0; 1,  0,  0,  0,  0]

Trifolium:
[-1,1]
5
[-1,1]
5
[0, 0, 0,-1, 1; 0, 0, 0, 0, 0; 0, 3, 2, 0, 0; 0, 0, 0, 0, 0; 1, 0, 0, 0, 0]

Astroid
[-1,1]
7
[-1,1]
7
[-1,  0,  3,  0, -3,  0,  1; 0,  0,  0,  0,  0,  0,  0; 3,  0, 21,  0,  3,  0,  0; 0,  0,  0,  0,  0,  0,  0; -3,  0,  3,  0,  0,  0,  0; 0,  0,  0,  0,  0,  0,  0; 1,  0,  0,  0,  0,  0,  0]
Luis Mendo
quelle
2
Noch lesbarer als Perl. Tolle Arbeit, auch ein netter Online-Compiler!
Fehler
@flawr lesbarer als Perl LOL. Für den Online-Compiler ist alles Suever's Arbeit!
Luis Mendo
1
@flawr Jetzt mit Faltung!
Luis Mendo
1
<3 Faltung!
Fehler