Traceur de courbes algébriques

14

Une courbe algébrique est un certain "sous-ensemble 1D" du "plan 2D" qui peut être décrit comme un ensemble de zéros {(x,y) in R^2 : f(x,y)=0 }d'un polynôme f. Ici, nous considérons le plan 2D comme le vrai plan de R^2sorte que nous pouvons facilement imaginer à quoi pourrait ressembler une telle courbe, essentiellement une chose que vous pouvez dessiner avec un crayon.

Exemples:

  • 0 = x^2 + y^2 -1 un cercle de rayon 1
  • 0 = x^2 + 2y^2 -1 une ellipse
  • 0 = xy une forme de croix , essentiellement l'union de l'axe des x et de l'axe des y
  • 0 = y^2 - x une parabole
  • 0 = y^2 - (x^3 - x + 1)une courbe elliptique
  • 0 = x^3 + y^3 - 3xy le folium de Descartes
  • 0 = x^4 - (x^2 - y^2) un lemniscate
  • 0 = (x^2 + y^2)^2 - (x^3 - 3xy^2) un trifolium
  • 0 = (x^2 + y^2 - 1)^3 + 27x^2y^2 un astroïde

Tâche

Étant donné un polynôme f(tel que défini ci-dessous) et les gammes x / y, produire une image en noir et blanc d'au moins 100x100 pixels qui montre la courbe sous forme de ligne noire sur fond blanc.

Détails

Couleur : Vous pouvez utiliser deux autres couleurs de votre choix, il devrait être facile de les distinguer.

Graphique : Au lieu d'une image en pixels, vous pouvez également produire cette image en tant qu'ascii-art, où les "pixels" d'arrière-plan doivent être des espaces / soulignements ou un autre caractère qui "semble vide" et la ligne peut être faite d'un caractère qui ressemble " plein "comme Mou Xou #.

Vous n'avez pas à vous soucier de l'alias.

Vous avez seulement besoin de tracer des lignes où le signe du polynôme change d'un côté à l'autre de la ligne (cela signifie que vous pouvez par exemple utiliser l'algorithme du carré de marche), vous n'avez pas à tracer correctement "les cas pathologiques comme 0 = x^2où le signe fait ne pas changer en allant d'un côté à l'autre de la ligne. Mais la ligne doit être continue et séparer les régions des différents signes de f(x,y).

Polynôme : Le polynôme est donné sous forme de (m+1) x (n+1)matrice / liste de listes de coefficients (réels), dans l'exemple ci-dessous les termes des coefficients sont donnés dans leur position:

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

Si vous préférez, vous pouvez supposer que la matrice est carrée (ce qui peut toujours être fait avec le remplissage nul nécessaire), et si vous le souhaitez, vous pouvez également supposer que la taille de la matrice est donnée comme entrées supplémentaires.

Dans ce qui suit, les exemples ci-dessus sont représentés comme une matrice définie comme ceci:

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]

Cas de test avec plage x / plage y:

(Dans un format pas tellement lisible mais meilleur copier-coller disponible ici sur pastebin .)

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]

J'ai eu l'inspiration pour quelques courbes de ce pdf.

flawr
la source
Est-ce que " vous n'avez pas à vous soucier de l'alias " signifie que nous pouvons simplement colorer chaque pixel selon que son centre se trouve sur la ligne?
Peter Taylor
Je ne vois pas le lien avec l'alias. Mais non, il devrait y avoir une ligne continue séparant les régions des différents signes.
flawr
La matrice n'est pas mx n, mais (m+1)x (n+1). Que prenons-nous comme entrée:, m, nou m+1,n+1? Ou pouvons-nous choisir?
Luis Mendo
Pouvons-nous simplement afficher la fonction graphique dans une nouvelle fenêtre?
R. Kap
1
@LuisMendo Oui, l'axe peut être dans n'importe quelle direction. (Tant qu'ils sont orthogonaux =)
flawr

Réponses:

10

Haskell, 283275 octets

La fonction g doit être appelée avec la matrice et les deux plages comme arguments. La matrice n'est qu'une liste de listes, les plages chacune une liste de deux éléments.

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

Voici les sorties pour les cas les plus intéressants: Notez que j'ai dû réduire la résultante de 100x100 à environ 40x40 de sorte qu'elle s'insère dans la console (changez simplement le 102 codé en dur en un nombre plus petit). Notez également que l'axe des y pointe vers le bas.

flawr
la source
Il y a quelques jolis petits golfs que vous pouvez faire ici. La dernière ligne utilise des parens lorsqu'elle peut être utilisée $pour enregistrer un octet. Les deux endroits où vous utilisez mappourraient être (<$>), et puisque vous n'utilisez qu'une efois que vous pouvez tirer à l' (0<)intérieur, c'est sa définition. ePeut également être nommé (!)pour économiser 3 octets.
Post Rock Garf Hunter
Et l'infixe zdans la définition de vvous permet de vous débarrasser de 4 parenthèses (autour z(&)et f g).
Post Rock Garf Hunter
Vous pouvez également renommer #un seul caractère (par exemple s) et le faire correspondre au modèle sur les listes au lieu de g. (par exemple 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 octets

Le bon outil pour le travail? J'utilise la manière intéressante que Matlab fait printfpour générer un polynôme sous forme de chaîne. Ce polynôme peut être fourni pour ezplottracer la courbe implicite sur le domaine spécifié. Pour plus de lisibilité, le code est présenté avec des nouvelles lignes après; ce qui n'est pas nécessaire et n'est pas pris en compte dans la taille.

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

Progression du golf sous forme d'extrait extensible.


Sortie des cas de test (cliquez pour une vue complète): Cas de test

algmyr
la source
2
Solution vraiment sympa en utilisant sprintf/ezplot!
flawr
Utiliser fixau lieu de floorpourrait vous aider à atteindre le nombre d'octets à deux chiffres :-)
Luis Mendo
Vous pouvez également utiliser [h,w]=size(A);t=0:h*w-1;pour enregistrer trois autres octets!
flawr
@LuisMendo En fait, je peux faire mieux. J'étais triste que printf de Matlab n'ait pas d'espace réservé entier, mais il supporte toujours des choses comme %.0f. Cela signifie que je peux laisser tomber le plancher et le printfréparer!
algmyr
@flawr J'utilise la deuxième partie de cela dans les itérations ultérieures. Je me rends compte que mon formatage avec la meilleure version dernière n'était pas parfaitement évident. Formatage modifié pour rendre ce cristal clair.
algmyr
6

Python 2, 261 octets

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))

Format d'entrée: matrix,xbounds,ybounds(par exemple [[-1,0,1],[0,0,0],[1,0,0]],[-2,2],[-2,2]). Format de sortie: PGM ordinaire .

Cela estime la distance entre chaque centre de pixels et la courbe en utilisant l'approximation de premier ordre d ( x , y ) = | p ( x , y ) | / | ∇ p ( x , y ) |, où ∇ p est le gradient du polynôme p . (Il s'agit de la distance entre ( x , y ) et l'intersection du plan tangent en ( x , y , p ( x , y )) avec le plan xy .) Puis les pixels où x , d (y ) est inférieure à une largeur de pixel de la courbe proportionnellement à d ( x , y ), résultant en de belles lignes anti-crénelées (même si ce n'est pas une exigence).

production

Voici les mêmes graphiques avec la fonction de distance divisée par 16 pour la rendre visible.

Anders Kaseorg
la source
Attendez, alors où dans le code le tracé graphique réel se produit-il?
R. Kap
@ R.Kap Le code écrit une image au format PGM ordinaire sur stdout. Il y a une printdéclaration pour l'en-tête de l'image et une printdéclaration dans la whileboucle pour la valeur de chaque pixel.
Anders Kaseorg
Wow, c'est vraiment cool! Pourriez-vous approfondir un peu votre algorithme de traçage?
flawr
@flawr J'ai un peu développé l'explication; Cela répond-t-il à vos questions?
Anders Kaseorg
@AndersKaseorg Oui, merci beaucoup!
flawr
5

Python 3.5 + MatPlotLib + Numpy, 352 octets:

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()

Une fonction nommée. Assez longtemps, mais bon, je suis juste content d'avoir pu accomplir la tâche. Prend 3 entrées, qui sont la m by nmatrice, la xplage et la yplage, qui doivent toutes être dans des tableaux (par exemple,[[-1,0,1],[0,0,0],[1,0,0]],[-2,2],[-2,2] ). Affiche le graphique terminé dans une nouvelle fenêtre graphique interactive. Va jouer au golf plus longtemps quand je peux, mais pour l'instant, j'en suis content.

Sorties finales pour les cas de test:

Sortie finale

R. Kap
la source
5

MATL , 67 61 octets

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

Ce code s'exécute dans la version 18.5.0 du langage, qui précède le défi. L'entrée utilise l'optionm , les nparamètres. La matrice a des points-virgules comme séparateurs de lignes. Le format d'entrée exact (en utilisant la parabole comme exemple) est

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

Le code produit une image de taille 255 × 255. Cela peut être testé en utilisant @Suever de » MATL en ligne compilateur, qui, entre autres fonctions très intéressantes, comprend la production graphique. Voir par exemple

Ce compilateur est encore à un stade expérimental. Veuillez signaler tout problème à @Suever dans le salon de discussion MATL . Si le bouton "Exécuter" ne fonctionne pas, essayez d'actualiser la page et de cliquer à nouveau.

Si vous préférez la sortie ASCII , le code doit être légèrement modifié (les changements n'affectent que les deux premiers et les quatre derniers caractères du code ci-dessus):

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

Cela produit une grille ASCII 100 × 100 qui utilise des caractères *pour représenter la courbe. Vous pouvez également tester cela avec @Dennis ' Essayez-le en ligne!Plate-forme:

Notez que le rapport d'aspect de la sortie ASCII est modifié car les caractères sont légèrement plus élevés que larges.

Explication

Le code calcule d'abord le polynôme à deux variables sur une grille x - y . Cela fait un usage intensif de la radiodiffusion , en calculant un tableau 4D intermédiaire où chaque dimension représente x valeurs, valeurs y , x exposants, y exposants.

À partir de cette fonction, la ligne de niveau zéro est calculée. Étant donné que le défi spécifie que seuls les changements de signe doivent être détectés, le code applique une convolution 2D avec un bloc de 2 × 2 et marque un pixel comme appartenant à la ligne sinon les quatre valeurs du bloc ont le même signe.

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)

Tous les cas de test

Voici toutes les entrées au format approprié, au cas où vous voudriez essayer:

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
la source
2
Encore plus lisible que Perl. Excellent travail, également un compilateur en ligne sympa!
flawr
@flawr plus lisible que Perl LOL. Quant au compilateur en ligne, c'est tout le travail de Suever!
Luis Mendo
1
@flawr Maintenant avec convolution!
Luis Mendo
1
<3 convolution!
flawr