Identifizieren Sie den Kegelschnitt

13

Bestimmen Sie bei 5 verschiedenen Punkten auf einer zweidimensionalen Ebene die Art des Kegelschnitts, der durch die Punkte gebildet wird. Der Ausgang wird einer der folgenden sein circle, hyperbola, ellipse, oder parabola.

Regeln

  • Die Punkte befinden sich im Allgemeinen in einer linearen Position, was bedeutet, dass keine drei Punkte kollinear sind und daher der durch sie verlaufende Kegel eindeutig ist.
  • Die Koordinaten der 5 Punkte sind Dezimalzahlen zwischen -10 und 10 einschließlich.
  • Die Genauigkeit für die Dezimal- / Gleitkommawerte sollte der Genauigkeit des systemeigenen Gleitkomma- / Dezimaltyps Ihrer Sprache entsprechen. Wenn Ihre Sprache / Ihr Datentyp eine willkürliche Genauigkeit hat, können Sie 12 Nachkommastellen als die maximal erforderliche Genauigkeit verwenden, die auf Null gerundet wird (z 1.0000000000005 == 1.000000000000. B. ).
  • Die Kapitalisierung der Ausgabe spielt keine Rolle.
  • Das Ausgeben, ellipsewenn der Kegelschnitt tatsächlich ein Kreis ist, ist nicht zulässig. Alle Kreise sind Ellipsen, Sie müssen jedoch die spezifischste ausgeben.

Bei Gleitkommaungenauigkeiten und -genauigkeiten:

Ich versuche, dies so einfach wie möglich zu gestalten, damit Probleme mit Gleitkommaungenauigkeiten nicht im Wege stehen. Das Ziel ist, wenn der Datentyp "magical infinite precision value" anstelle von float / double wäre, würde alles perfekt funktionieren. Da es jedoch keinen "magischen unendlichen Genauigkeitswert" gibt, schreiben Sie Code, der davon ausgeht, dass Ihre Werte unendliche Genauigkeit haben, und alle Probleme, die aufgrund von Gleitkommaungenauigkeiten auftreten, sind Funktionen, keine Fehler.

Testfälle

(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
quelle
2
Bei Floats circlescheint es für Ausgaben wie erforderlich zu sein, die Gleichheit der Floats zu überprüfen, um sie von einer sehr runden Ellipse zu unterscheiden. Welche Präzision sollen wir hier annehmen?
XNOR
1
@Mego Warum nicht die ganzzahlige Version des Problems für alle Sprachen zulassen, aber mit einem größeren Bereich, z. B. -10000 bis 10000.
orlp
1
Sind Sie sicher, dass Testfall vier korrekt ist? desmos: desmos.com/calculator/fmwrjau8fd
Maltysen
2
Auch 3 sieht falsch aus: desmos.com/calculator/tkx1wrkotd
Maltysen
1
Ich denke, Sie verstehen die Probleme mit der FP-Genauigkeit, und das führt zu einer Antwort wie dieser: codegolf.stackexchange.com/a/77815/21348
edc65

Antworten:

2

Matlab, 154 Bytes

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

Dank Suevers Vorschlägen konnten einige Bytes eingespart werden.

Übernimmt die Eingabe als [x1 y1;x2 y2;x3 y3; etc]. Dies verwendete eine Vandermonde-Matrix und fand die Basis ihres Nullraums, der immer ein einzelner Vektor sein wird. Dann berechnet es die Diskriminante und verwendet sie, um einen Index zwischen 1 und 4 zu erstellen, der zum Abrufen der Zeichenfolge verwendet wird.

Ungolfed:

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

Der sign(...)Teil berechnet die Diskriminante und gibt 1, wenn sie positiv ist (Hyperbel), -1, wenn sie negativ ist (Ellipse) und 0, wenn sie 0 ist (Parabel). Der max(...)subtrahiert 1 weg, wenn es ein Kreis ist. Matlab-Arrays sind einseitig indiziert. Fügen Sie also 3 hinzu, um die Werte 1, 2, 3, 4 zu erhalten, und indizieren Sie damit das Array mit den Namen der konischen Abschnitte.

David
quelle
1
Anstatt zu vergleichen max() == 0, können Sie zu vereinfachen~max()
Suever
1
Auch anstelle von ones(length(p),1)Ihnen könnte tun1+p(:,1)*0
Suever
Prost, die max()Sache war albern von mir, ich hatte dort schon Vergleiche und wurde offensichtlich faul! Diese Art, das zu bekommen, onesist auch sehr schön.
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'

Jede Sprache, die besser für den Umgang mit Matrix und Determinante geeignet ist, sollte besser abschneiden (APL, J, CJAM, Jelly).

Literatur: Allgemeine Form eines Kegels , Fünf Punkte bestimmen einen Kegel , Lineares Gleichungssystem , Determinante

In der kartesischen Ebene ist die allgemeine Gleichung eines Kegels

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

mit A oder B oder C ungleich 0 (sonst ist es eine gerade Linie)

A ... F sind sechs Unbekannte zu finden. Mit fünf Paaren von (x, y) können wir ein lineares System mit fünf Gleichungen aufbauen und durch Skalieren eine Dimension entfernen. Das heißt, wir können eines von A, B oder C auf 1 setzen, wenn es nicht 0 ist (und wir wissen, dass mindestens eines nicht 0 ist).

Ich baue und versuche 3 Systeme zu lösen: Zuerst versuche ich A = 1. Wenn nicht lösbar, dann B = 1, dann C. (Es könnte einen besseren Weg geben, aber das ist mein bester zu der Zeit)

Mit den Werten von A, B, C können wir den Kegel klassifizieren, der die Diskriminante betrachtet d=B*B-4*A*C

  • d == 0 -> Parabel
  • d> 0 -> Hyperbel
  • d 0 - Ellipse, insbesondere (A == C und B == 0) -> Kreis

Weniger golfen

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

Prüfung

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
quelle
2
Das ist wirklich schön! Ausgezeichnete Arbeit!
Alex A.
2

Python - 234 Bytes

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"

Ich drucke nie circleoder parabolaweil tund d[1]traf nie genau 0, aber OP sagte, das sei in Ordnung.

Maltysen
quelle
1

C 500

Meine JavaScript-Antwort wurde auf C portiert. Nur um zu sehen, ob dies möglich ist.

Verwendung: 10 Werte von der Standardeingabe lesen

echo 1 0 0 1 2 1 3 4 4 9 | konisch

Ausgabe:

Parabel

Test (ideone)

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");}

Weniger golfen

// 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
quelle
1

Salbei, 247 Bytes

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

Probieren Sie es online aus

Diese Funktion nimmt einen iterable von (x,y)Paaren als Eingabe versucht die Diskriminante von jeder der möglichen linearen 3 Systemen Berechnen ( A=1, B=1, und C=1), und gibt den Typ des konischen Abschnitts auf der Grundlage der Werte der Diskriminanzfunktion, A, B, und C.

Es ist wahrscheinlich noch ein bisschen mehr Golf zu spielen, aber ich bin mit Sage verrostet und müde, also werde ich am nächsten Morgen weiter daran arbeiten.

Mego
quelle