Punto nello scafo convesso (2D)


10

sfondo

Lo scafo convesso di un numero finito di punti è il poligono convesso più piccolo che contiene tutti i punti, sia come vertici che all'interno. Per ulteriori informazioni, vedere questa domanda su PGM che lo definisce molto bene .

Ingresso

N+1Coordinate 2-D ( N >= 3) passate STDIN(con gli altri consueti input di golf consentiti) nel seguente formato (il numero di decimali può variare ma si può presumere che rimanga "ragionevole" e ogni numero può essere rappresentato come un float):

0.00;0.00000
1;0.00
0.000;1.0000
-1.00;1.000000

Produzione

Un valore di verità stampato su STDOUT(o equivalente) se il primo punto dell'elenco ( (0.00;0.00000)nell'esempio sopra) si trova nello scafo convesso degli altri N punti e un valore di falsa in caso contrario.

Questo è , quindi vince la soluzione più breve in byte.

  • Casi di bordo : puoi restituire qualsiasi valore (ma non schiantarti) se il punto si trova sul bordo dello scafo convesso (cioè su un lato o su un vertice sulla frontiera esterna dello scafo), poiché è una probabilità zero evento (con ogni ragionevole probabilità).

  • Proibito : qualsiasi cosa (linguaggio, operatore, struttura dati, built-in o pacchetto) che esiste solo per risolvere problemi geometrici (ad esempio ConvexHull di Mathematica ). Sono ammessi strumenti matematici di uso generale (vettori, matrici, numeri complessi, ecc.).

test


3
Che cos'è una "struttura di dati ad hoc"?
DavidC,

"funzioni / operatori elementari" è troppo vago.
xnor

@DavidCarraher: qualcosa come un poligono, un triangolo o un segmento (tutto ciò che esiste solo per risolvere problemi geometrici).
Alexandre Halm,

2
@AlexandreHalm La tua modifica ha aiutato molto. Penso che "elementare" non sia la parola giusta. Ho pensato che avrebbe eliminato gli incorporamenti generici come sorto round. Penso che sia più chiaro dire semplicemente che non è permesso nulla di specifico per la geometria. Che dire, però, di una funzione per aggiungere due elenchi come vettori? O una funzione per trovare l'argomento (angolo) di un numero complesso?
xnor

1
Questo è il motivo per cui i diamanti richiedono alle persone di utilizzare Sandbox prima di pubblicare nuove sfide.
cat

Risposte:


9

J, 40 39 34 byte

3 :'(o.1)<(>./-<./)12 o.y*+{.y'@:-

Una funzione diadica anonima, prendendo un punto, p , come uno dei suoi argomenti, e un elenco di punti, P , come l'altro argomento (non importa quale argomento sia quale), e restituendo 0o 1, se p è esterno o all'interno dello scafo convesso di P , rispettivamente. Il punto p , e i punti in P , sono presi come numeri complessi.

Esempio

  is_inside =: 3 :'(o.1)<(>./-<./)12 o.y*+{.y'@:-

  0.5j0.5  is_inside  0j0 0j1 1j0 1j1
1
  1.5j0.5  is_inside  0j0 0j1 1j0 1j1
0

o...

Python 2, funzione, 121 103, programma completo, 162

Python 3, 149 byte

import sys,cmath as C
p,q,*P=[complex(*eval(l.replace(*";,")))for l in sys.stdin]
A=[C.phase((r-p)/(q-p+(q==p)))for r in P]
print(max(A)-min(A)>C.pi)

Riceve input, nello stesso formato del post originale, tramite STDIN e stampa un valore booleano che indica se p è nello scafo convesso di P


Spiegazione

Il programma verifica se la differenza tra gli angoli massimo e minimo (con segno) tra qualsiasi punto r in P , p e un punto arbitrario fisso q in P (usiamo solo il primo punto in P ), è inferiore a 180 °. In altre parole, verifica se tutti i punti in P sono contenuti in un angolo di 180 ° o inferiore, attorno a p . p è nello scafo convesso di P se e solo se questa condizione è falsa.


Al costo di qualche byte in più, possiamo usare un metodo simile che non ci richiede di calcolare esplicitamente gli angoli: Nota che la condizione sopra è equivalente a dire che p è al di fuori dello scafo convesso di P se e solo se esiste una linea da l a p , in modo tale che tutti i punti in P siano sullo stesso lato di l . Se esiste una tale linea, allora c'è anche una tale linea che è incidente a uno (o più) dei punti in P (possiamo ruotare l finché non tocca uno dei punti in P. )

Per (provvisoriamente) trovare questa linea, partiamo lasciando l la retta passante per p e il primo punto in P . Quindi ripetiamo il resto dei punti in P ; se uno dei punti è alla sinistra di l (assumiamo una certa direzionalità in tutto, sinistra o destra non contano davvero), sostituiamo l con la linea che passa attraverso p e quel punto e continuiamo. Dopo aver ripetuto tutta P , se (e solo se) p è al di fuori dello scafo convesso, tutti i punti in P dovrebbero essere a destra di (o su) l . Verifichiamo che usando un secondo passaggio sui punti in P.

Python 2, 172 byte

import sys
P=[eval(l.replace(*";,"))for l in sys.stdin]
x,y=P.pop(0)
C=lambda(a,b),(c,d):(a-x)*(d-y)-(b-y)*(c-x)>0
l=reduce(lambda*x:x[C(*x)],P)
print any(C(l,q)for q in P)


In alternativa, per fare la stessa cosa in un unico passaggio, lasciate alla-sinistra di una realtion tra qualsiasi due punti, q ed r , in P , tale che q è a sinistra di r se q è a sinistra della linea che passa attraverso p e r . Si noti che per-il-sinistro di è un rapporto ordine su P se e solo se tutti i punti P sono sullo stesso lato di qualche linea passante per p , cioè, se p è esterno convesso di P . La procedura sopra descritta trova il punto minimo in PWRT questo ordine, cioè, il punto "sinistra" in P . Invece di eseguire due passaggi, possiamo trovare il massimo (ovvero il punto "più a destra"), nonché il minimo, i punti in P rispetto allo stesso ordine in un singolo passaggio e verificare che il minimo sia a sinistra del massimo, cioè efficacemente, che alla sinistra di è transitivo.

Funzionerebbe bene se p è fuori dallo scafo convesso di P , nel qual caso a sinistra di è in realtà una relazione d'ordine, ma potrebbe rompersi quando p è all'interno dello scafo convesso (ad esempio, provare a capire cosa sarà succede se abbiamo eseguito questo algoritmo in cui i punti in P sono i vertici di un pentagono regolare, correndo in senso antiorario, e p è il suo centro.) Per adattarlo, modifichiamo leggermente l'algoritmo: Selezioniamo un punto q in P e bisecare P lungo la linea che passa attraverso p e q (cioè partizioniamo P attorno a qwrt to-the-left-of.) Ora abbiamo una "parte sinistra" e una "parte destra" di P , ciascuna contenuta in un mezzo piano, in modo che a sinistra di una relazione d'ordine su ciascuna; troviamo il minimo della parte sinistra e il massimo della parte destra e li confrontiamo come descritto sopra. Ovviamente, non dobbiamo dividere in due fisicamente P , possiamo semplicemente classificare ogni punto in P mentre cerchiamo il minimo e il massimo, in un singolo passaggio.

Python 2, 194 byte

import sys
P=[eval(l.replace(*";,"))for l in sys.stdin]
x,y=P.pop(0)
C=lambda(a,b),(c,d):(a-x)*(d-y)-(b-y)*(c-x)>0
l=r=P[0]
for q in P:
 if C(P[0],q):l=q*C(l,q)or l
 elif C(q,r):r=q
print C(l,r)

qualche possibilità che tu possa realizzare le tue soluzioni (almeno quella di Python, non ho idea se J possa farlo) prendendo input da STDIN? Trovo che sarebbe più facile confrontare le soluzioni con condizioni di parità. Supponendo che l'input sia già un insieme preformattato di numeri o punti complessi è un po 'un tratto IMO.
Alexandre Halm,

@AlexandreHalm Aggiunto programma completo.
Ell,

Dovresti dividere le tue soluzioni in una risposta per lingua.
Mego

4

Ottava, 82 72 byte

d=dlmread(0,";");i=2:rows(d);~isna(glpk(i,[d(i,:)';~~i],[d(1,:)';1]))&&1

L'idea è di verificare se il programma lineare min {c'x: Ax = b, e'x = 1, x> = 0} ha una soluzione, dove e è un vettore di tutti, le colonne di A sono le coordinate di la nuvola di punti eb è il punto di prova e c è arbitraria. In altre parole, proviamo a rappresentare b come una combinazione convessa di colonne di A.

Per eseguire lo script, utilizzare octave -f script.m <input.dat


2

R, 207 byte

d=read.csv(file("stdin"),F,";")
q=function(i,j,k)abs(det(as.matrix(cbind(d[c(i,j,k),],1))))
t=function(i,j,k)q(i,j,k)==q(1,i,j)+q(1,i,k)+q(1,j,k)
any(apply(combn(2:nrow(d),3),2,function(v)t(v[1],v[2],v[3])))

Lo script prende i suoi input da STDIN, ad es Rscript script.R < inputFile.

Genera tutti i triangoli dagli Nultimi punti (l'ultima riga apply(combn(...) e controlla se il primo punto si trova nel triangolo usando la tfunzione.

tusa il metodo area per decidere se Uè in ABC: (scrivere (ABC)per l'area di ABC) Uis in ABCiff (ABC) == (ABU) + (ACU) + (BCU). Inoltre, le aree sono calcolate usando la formula determinante (vedi qui per una bella demo di Wolfram).

Sospetto che questa soluzione sia più soggetta a errori numerici rispetto alla mia altra, ma funziona sui miei casi di test.


0

R, 282 byte

d=read.csv(file("stdin"),F,";")
p=function(a,b)a[1]*b[1]+a[2]*b[2]
t=function(a,b,c){A=d[a,];
U=d[1,]-A
B=d[b,]-A
C=d[c,]-A
f=p(C,C)
g=p(B,C)
h=p(U,C)
i=p(B,B)
j=p(U,B)
k=f*i-g*g
u=i*h-g*j
v=f*j-g*h
min(u*k,v*k,k-u-v)>0}
any(apply(combn(2:nrow(d),3),2,function(v)t(v[1],v[2],v[3])))

Lo script prende i suoi input da STDIN, ad es Rscript script.R < inputFile.

Genera tutti i triangoli dagli Nultimi punti (l'ultima riga apply(combn(...) e controlla se il primo punto si trova nel triangolo usando la tfunzione.

tutilizza il metodo baricentrico decidere se Uè in ABC: (scrivendo XYper la Xdi Yvettore) poiché (AB,AC)è una base per il piano (ad eccezione dei casi degeneri in cui sono allineati A, B, C), AUpuò essere scritta come AU = u.AB + v.ACe Uè nel sse triangolo u > 0 && v > 0 && u+v < 1. Vedi ad esempio qui per una spiegazione più dettagliata e un bel grafico interattivo. NB: per salvare un paio di caratteri e gli errori evitano DIV0, abbiamo solo calcoliamo un collegamento a ued ve un test modificato ( min(u*k,v*k,k-u-v)>0).

Gli unici operatori matematici utilizzati sono +, -, *, min()>0.

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.