Identifier la section conique

13

Étant donné 5 points distincts sur un plan bidimensionnel, déterminez le type de section conique formée par les points. La sortie est un des circle, hyperbola, ellipseou parabola.

Règles

  • Les points seront en position linéaire générale, ce qui signifie qu'il n'y a pas trois points colinéaires, et donc la conique qui les traverse sera unique.
  • Les coordonnées des 5 points seront des nombres décimaux compris entre -10 et 10 inclus.
  • La précision des valeurs décimales / flottantes doit être la précision du type natif flottant / décimal de votre langue. Si votre langue / type de données est à précision arbitraire, vous pouvez utiliser 12 chiffres après la virgule comme précision maximale requise, en arrondissant vers zéro (par exemple 1.0000000000005 == 1.000000000000).
  • La capitalisation de la production n'a pas d'importance.
  • La sortie ellipselorsque la section conique est en fait un cercle n'est pas autorisée. Tous les cercles sont des ellipses, mais vous devez produire le plus spécifique.

Sur les imprécisions et la précision en virgule flottante:

J'essaie de rendre cela aussi simple que possible, afin que les problèmes d'inexactitudes en virgule flottante ne gênent pas. Le but est que si le type de données était "valeur de précision infinie magique" au lieu de float / double, alors tout fonctionnerait parfaitement. Mais, comme la "valeur de précision infinie magique" n'existe pas, vous écrivez du code qui suppose que vos valeurs sont d'une précision infinie, et tous les problèmes qui surviennent à la suite d'inexactitudes en virgule flottante sont des fonctionnalités, pas des bogues.

Cas de test

(0, 0), (1, 5), (2, 3), (4, 8), (9, 2) => hyperbola
(1.2, 5.3), (4.1, 5.6), (9.1, 2.5), (0, 1), (4.2, 0) => ellipse
(5, 0), (4, 3), (3, 4), (0, 5), (0, -5) => circle
(1, 0), (0, 1), (2, 1), (3, 4), (4, 9) => parabola
Mego
la source
2
Pour les flottants, les sorties comme circlesemblent nécessiter la vérification de l'égalité des flotteurs pour se distinguer d'une ellipse très ronde. Quelle précision faut-il assumer ici?
xnor
1
@Mego Pourquoi ne pas autoriser la version entière du problème pour toutes les langues, mais avec une plage plus large, par exemple -10000 à 10000.
orlp
1
êtes-vous sûr que le cas de test quatre est correct? desmos: desmos.com/calculator/fmwrjau8fd
Maltysen
2
De plus, 3 semble faux aussi: desmos.com/calculator/tkx1wrkotd
Maltysen
1
Je pense que vous sous-estimez les problèmes de précision de FP, et cela conduit à répondre comme ceci codegolf.stackexchange.com/a/77815/21348
edc65

Réponses:

2

Matlab, 154 octets

p=input();c=null([p.^2 prod(p,2) p 1+p(:,1)*0]),s={'circle' 'ellipse' 'parabola' 'hyperbola'};s{3+sign(c(3)^2-4*c(1)*c(2))-~max(abs(c(3)),abs(c(1)-c(2)))}

Enregistré quelques octets grâce aux suggestions de Suever.

Prend l'entrée comme [x1 y1;x2 y2;x3 y3; etc]. Celui-ci a utilisé une matrice Vandermonde, et trouve la base de son espace nul, qui sera toujours un seul vecteur. Ensuite, il calcule le discriminant et l'utilise pour créer un index entre 1 et 4 qui est utilisé pour obtenir la chaîne.

Non golfé:

p=input();
c=null([p.^2 prod(p')' p ones(length(p),1)]);
s={'circle' 'ellipse' 'parabola' 'hyperbola'};
s{3+sign(c(3)^2-4*c(1)*c(2))-~max(abs(c(3)),abs(c(1)-c(2)))}

La sign(...)partie calcule le discriminant, en donnant 1 si elle est positive (hyperbole), -1 si elle est négative (ellipse) et 0 si elle est 0 (parabole). Le max(...)soustrait 1 si c'est un cercle. Les tableaux Matlab sont à un index, alors ajoutez 3 pour donner les valeurs 1, 2, 3, 4 et utilisez-les pour indexer le tableau des noms de sections coniques.

David
la source
1
Plutôt que de comparer, max() == 0vous pouvez simplifier~max()
Suever
1
Aussi, au lieu de ones(length(p),1)vous pourriez le faire1+p(:,1)*0
Suever
À la vôtre, la max()chose était idiote de ma part, j'ai déjà fait des comparaisons avant et je suis devenu paresseux évidemment! Cette façon d'obtenir le onesest également très agréable.
David
14

JavaScript (ES6), 316 323 347

p=>[1,2,4].some(x=>(d=D(Q=[[x&1,x&2,x&4,0,0,0],...p.map(([x,y])=>[x*x,x*y,y*y,x,y,1])]))?[a,b,c]=Q.map((v,i)=>D(Q.map((r,j)=>(r=[...r],r[i]=x*!j,r)))/d):0,D=m=>m[1]?m[0].reduce((r,v,i)=>r+(i&1?-v:v)*D(m.slice(1).map(r=>r.filter((a,j)=>j-i))),0):m)&&(d=b*b-4*a*c)?d<0?!b&c==a?'Circle':'Ellipse':'Hyperbola':'Parabola'

Tout langage mieux adapté à la gestion de la matrice et du déterminant devrait avoir un meilleur score (APL, J, CJAM, Jelly)

Références: Forme générale d'une conique , Cinq points déterminent une conique , Système d'équations linéaires , Déterminant

Dans le plan cartésien, l'équation générale d'une conique est

A*x*x + B*x*y + C*y*y + D*x + E*y + F = 0 

ayant A ou B ou C différent de 0 (sinon c'est une ligne droite)

A ... F sont six inconnues à trouver. Avec cinq paires de (x, y), nous pouvons construire un système linéaire avec cinq équations, et la mise à l'échelle supprime une dimension. Autrement dit, nous pouvons définir l'un de A, B ou C sur 1 si ce n'est pas 0 (et nous savons qu'au moins un n'est pas 0).

Je construis et essaie de résoudre 3 systèmes: d'abord essayer A = 1. Si non résoluble alors B = 1, puis C. (Il pourrait y avoir une meilleure façon, mais c'est mon meilleur à l'époque)

Ayant les valeurs de A, B, C on peut classer la conique en regardant le discriminant d=B*B-4*A*C

  • d == 0 -> parabole
  • d> 0 -> hyperbole
  • d <0 -> ellipse, en particulier (A == C et B == 0) -> cercle

Moins golfé

F=p=>(
  // Recursive function to find determinant of a square matrix
  D=m=>m[1]
    ?m[0].reduce((r,v,i)=>r+(i&1?-v:v)*D(m.slice(1).map(r=>r.filter((a,j)=>j-i))),0)
    :m,
  // Try 3 linear systems, coefficients in Q
  // Five equation made from the paramaters in p
  // And a first equation with coefficient like k,0,0,0,0,0,1 (example for A)  
  [1,2,4].some(
    x => (
      // matrix to calc the determinant, last coefficient is missing at this stage
      Q = [ 
        [x&1, x&2, x&4, 0,0,0] // first one is different
        // all other equations built from the params 
        ,...p.map( ([x,y]) => [x*x, x*y, y*y, x, y, 1] )
      ],
      d = D(Q), // here d is the determinant
      d && ( // if solvable  then d != 0
        // add missing last coefficient to Q
        // must be != 0 for the first row, must be 0 for the other
        Q.map( r=> (r.push(x), x=0) ),
        // solve the system (Cramer's rule), I get all values for A...F but I just care of a,b,c
        [a,b,c] = Q.map((v,i)=>D(Q.map(r=>(r=[...r],r[i]=r.pop(),r))) / d),
        d = b*b - 4*a*c, // now reuse d for discriminant
        d = d<0 ? !b&c==a ? 'Circle' : 'Ellipse' // now reuse d for end result
        : d ? 'Hyperbola' : 'Parabola'
      ) // exit .some if not 0
    ), d // .some exit with true, the result is in d
  )  
)

Tester

F=p=>[1,2,4].some(x=>(d=D(Q=[[x&1,x&2,x&4,0,0,0],...p.map(([x,y])=>[x*x,x*y,y*y,x,y,1])]))?[a,b,c]=Q.map((v,i)=>D(Q.map((r,j)=>(r=[...r],r[i]=x*!j,r)))/d):0,D=m=>m[1]?m[0].reduce((r,v,i)=>r+(i&1?-v:v)*D(m.slice(1).map(r=>r.filter((a,j)=>j-i))),0):m)&&(d=b*b-4*a*c)?d<0?!b&c==a?'Circle':'Ellipse':'Hyperbola':'Parabola'

console.log=(...x)=>O.textContent+=x+'\n'

;[
 [[0, 0], [1, 5], [2, 3], [4, 8], [9, 2]]
,[[1.2, 5.3],[4.1, 5.6], [9.1, 2.5], [0, 1], [4.2, 0]]
,[[5, 0], [4, 3], [3, 4], [0, 5], [0, -5]]
,[[1, 0], [0, 1], [2, 1], [3, 4], [4, 9]]
].forEach(t=>console.log(t.join`|`+' => '+F(t)))
<pre id=O></pre>

edc65
la source
2
C'est vraiment sympa! Excellent travail!
Alex A.
2

Python - 234 octets

import numpy as n
x=input()
d=[n.linalg.det(n.delete(n.array([[i*i,i*j,j*j,i,j,1]for i,j in x]),k,1))for k in range(6)]
t=d[1]**2-4*d[0]*d[2]
print"hyperbola"if t>0else"parabola"if t==0else"circle"if d[1]==0and d[0]==d[2]else"ellipse"

Je ne ai jamais imprimer circleou parabolaparce que tet d[1]ne jamais frapper exactement 0, mais OP dit était correct.

Maltysen
la source
1

C, 500

Ma réponse JavaScript portée sur C. Juste pour voir si cela peut être fait.

Utilisation: lire 10 valeurs à partir de l'entrée standard

écho 1 0 0 1 2 1 3 4 4 9 | conique

Production:

Parabole

Test (idéone)

double D(m,k)double*m;{double t=0;for(int s=1,b=1,x=0;x<6;x++,b+=b)k&b||(t+=s*m[x]*(k+b>62?1:D(m+6,k+b)),s=-s);return t;}i,u,h;double m[36],*t=m+6,w[6],s[3],b,d;main(){for(;i++<5;*t++=d*d,*t++=d*b,*t++=b*b,*t++=d,*t++=b,*t++=1)scanf("%lf%lf",&d,&b);for(u=4;u;u/=2)for(m[0]=u&1,m[1]=u&2,m[2]=u&4,d=D(m,0),h=0;d&&h<3;h++){for(i=0;i<6;i++)w[i]=m[i*6+h],m[i*6+h]=i?0:u;s[h]=D(m,0)/d;for(;i--;)m[i*6+h]=w[i];}b=s[1];d=b*b-4*s[0]*s[2];puts(d?d<0?!b&(s[2]==s[0])?"Circle":"Ellipse":"Hyperbola":"Parabola");}

Moins golfé

// Calc determinant of a matrix of side d
// In the golfed code, d is fix to 6
double D(m, d, k)
double*m;
{
    int s = 1, b = 1, x = 0;
    double t = 0;
    for (; x < d; x++, b += b)
        k&b || (
            t += s*m[x] *(k+b+1==1<<d? 1: D(  m + d, d, k + b)), s = -s
        );
    return t;
}

double m[36],d, *t = m + 6, w[6], s[3], a, b, c;
i,u,h;
main()
{
    for (; i++ < 5; )
    {
        scanf("%lf%lf", &a, &b);
        *t++ = a*a, *t++ = a*b, *t++ = b*b, *t++ = a, *t++ = b, *t++ = 1;
    }
    for (u = 4; u; u /= 2)
    {
        m[0] = u & 1, m[1] = u & 2, m[2] = u & 4;
        d = D(m, 6, 0);
        if (d) 
            for (h = 0; h < 3; h++)
            {
                for (i = 0; i < 6; i++)
                    w[i] = m[i * 6 + h],
                    m[i * 6 + h] = i ? 0 : u;
                s[h] = D(m, 6, 0)/d;
                for (; i--; )
                    m[i * 6 + h] = w[i];
            }
    }
    a = s[0], b = s[1], c = s[2];
    d = b*b - 4 * a * c;
    puts(d ? d < 0 ? !b&(c == a) ? "Circle" : "Ellipse" : "Hyperbola" : "Parabola");
}
edc65
la source
1

Sauge, 247 octets

def f(p):
 for i in[1,2,4]:
  z=[i&1,i&2,i&4,0,0,0]
  M=matrix([z]+[[x*x,x*y,y*y,x,y,1]for x,y in p])
  try:A,B,C=(M\vector(z))[:3]
  except:continue
  d=B*B-4*A*C
  return['parabola','hyperbola','circle','ellipse'][[d==0,d>0,d<0and B==0and A==C,d<0].index(1)]

Essayez-le en ligne

Cette fonction prend une iterable de (x,y)paires en entrée, le calcul essaie le discriminant de chacun des 3 systèmes linéaires possibles ( A=1, B=1, et C=1), et délivre en sortie le type de section conique sur la base des valeurs de discriminant, A, B, et C.

Il y a probablement encore du golf à faire, mais je suis rouillé avec Sage et somnolent en ce moment, donc je vais y travailler plus le matin.

Mego
la source