Trova l'area del più grande poligono convesso


29

Dato un elenco di coordinate intere, trova l'area del più grande poligono convesso che puoi costruire dall'elenco in modo tale che -

  • ogni vertice è nella lista
  • nessun elemento dell'elenco è contenuto all'interno del poligono.

Esempio:

(0, 0) (8, 0) (0, 1) (3, 1) (7, 1) (1, 2) (5, 2) (9, 2) (2, 3) (5, 3) (7, 3) (3, 4) (5, 5) (11, 5)

visualizzati:

o       o
o  o   o
 o   o   o
  o  o o
   o
     o     o

Il più grande poligono convesso che puoi fare da questo è questo:

o     
o  o  
 o   o
  o  o
   o
     o

Con una superficie di 12.


Puoi prendere la lista delle coordinate in qualsiasi formato ragionevole, e dovresti generare (in modo appropriato per la tua lingua preferita) l'area del poligono convesso più grande, arrotondata a non meno di 2 cifre dopo il punto decimale.

Inoltre, è necessario utilizzare una sorta di algoritmo e non semplicemente forzare tutti i sottoinsiemi di punti. Per far questo, il tuo programma deve risolvere un elenco di 50 vertici in meno di un minuto su un PC moderno.

Vince il codice più breve in byte.


Conosci un algoritmo veloce per il caso peggiore?
xnor

3
Se vuoi imporre un limite di tempo su 100 vertici, dovresti probabilmente includere almeno uno di questi casi di test (idealmente diversi, ad esempio uno in cui tutti i 100 vertici fanno parte della soluzione, uno in cui 99 sono e uno in cui solo 10 lo sono) .
Martin Ender,

@ MartinBüttner Purtroppo, non riesco a generare questo caso di test in quanto non ho un'implementazione funzionante. Il problema è piuttosto complicato :)
orlp

@xnor Un paio di esempi possono essere trovati qui .
orlp,

"arrotondato a non meno di 2 cifre dopo il punto decimale"?
DavidC

Risposte:


12

Javascript ES6, 738 byte

((V,C,L,r,k,n,A,G,F,e,i,j,q)=>p=>{p=p.map((p,i)=>({i:i,x:p[0],y:p[1]}));A=(f,p,a,b,v,i)=>{for(i=p[n],v=V(a,b);i--;)if(f(v,V(a,p[i])))return 1};G=(p,i,a)=>{for(i=p[n]-1,a=C(p[i],p[0]);i--;)a+=C(p[i],p[i+1]);if((a/=2)>r)r=a};F=(p,s,l,f,a,b,v)=>(l=s[n],f=s[0],a=s[l-2],b=s[l-1],e[a.i][b.i]||A((a,b)=>C(a,b)?0:a.x<0==b.x<0&&a.y<0==b.y<0&&L(a)>L(b),p,a,b)?0:(p=(v=V(a,b),p[k](x=>C(v,V(a,x))>=0)),A((a,b)=>C(a,b)>0,p,b,f)?0:(p.map(q=>F(p[k](r=>q!==r),[...s,q])),s[2]&&!p[n]&&!e[b.i][f.i]?G(s):0)));e=p.map(x=>p.map(y=>x===y));for(i=p[n];i--;){for(j=i;j--;){q=p[k]((p,x)=>x-i&&x-j);F(q,[p[i],p[j]]);F(q,[p[j],p[i]]);e[i][j]=e[j][i]=1}}console.log(r)})((a,b)=>({x:b.x-a.x,y:b.y-a.y}),(a,b)=>a.x*b.y-a.y*b.x,v=>v.x*v.x+v.y*v.y,0,'filter','length')

Ecco una versione ES5 o inferiore che dovrebbe funzionare nella maggior parte dei browser e dei nodi senza modifiche: 827 byte

eval("(%V,C,L,r,k,n,A,G,F,e,i,j,q){@%p){p=p.map(%p,i){@{i:i,x:p[0],y:p[1]}});A=%f,p,a,b,v,i){for(i=p[n],v=V(a,b);i--;)if(f(v,V(a,p[i])))@1};G=%p,i,a){for(i=p[n]-1,a=C(p[i],p[0]);i--;)a+=C(p[i],p[i+1]);if((a/=2)>r)r=a};F=%p,s,l,f,a,b,v){@(l=s[n],f=s[0],a=s[l-2],b=s[l-1],e[a.i][b.i]||A(%a,b){@C(a,b)!=0?0:a.x<0==b.x<0&&a.y<0==b.y<0&&L(a)>L(b)},p,a,b)?0:(p=(v=V(a,b),p[k](%x){@C(v,V(a,x))>=0})),A(%a,b){@C(a,b)>0},p,b,f)?0:(p.forEach(%q){@F(p[k](%r){@q!==r}),s.concat([q]))}),s[2]&&p[n]==0&&!e[b.i][f.i]?G(s):0)))};e=p.map(%x,i){@p.map(%y,j){@i==j})});for(i=p[n];i--;){for(j=i;j--;){q=p[k](%p,x){@x!=i&&x!=j});F(q,[p[i],p[j]]);F(q,[p[j],p[i]]);e[i][j]=e[j][i]=1}}console.log(r)}})(%a,b){@{x:b.x-a.x,y:b.y-a.y}},%a,b){@a.x*b.y-a.y*b.x},%v){@v.x*v.x+v.y*v.y},0,'filter','length')".replace(/%/g,'function(').replace(/@/g,'return '))

Il codice restituisce una funzione anonima. Come parametro, prende una serie di punti, come [[0,1],[2,3],[4,5]]. Per usarlo puoi posizionarlo var f=prima di esso, o se vuoi usarlo dalla riga di comando, aggiungi (process.argv[2].replace(/ /g,'').slice(1,-1).split(')(').map((x)=>x.split(',')))alla fine e chiamalo comenode convpol.js '(1,2)(3,4)(5,6)'

Grazie per la sfida! Poiché non esiste un'implementazione di riferimento, non posso dimostrare che ciò sia corretto, ma è coerente almeno per le permutazioni dell'elenco punti. Quasi non pensavo che avrebbe funzionato, poiché le versioni con codice di debug, anche disabilitate, erano troppo lente con un aumento esponenziale del tempo. Ho deciso comunque di giocare a golf, e sono stato contento di vedere che è sceso a meno di 2 secondi per 50 punti sulla mia macchina. Può calcolare circa 130 punti in 1 minuto.

L'algoritmo è simile alla scansione di Graham , tranne per il fatto che deve cercare ovunque scafi convessi vuoti.

Spiegazione

Ecco una panoramica di alto livello su come funziona l'algoritmo. La sostanza di questo algoritmo è solo la ricerca di anelli convessi in senso antiorario che non racchiudano un punto. La procedura è simile a questa:

  1. Inizia con una coppia di punti e un elenco di tutti gli altri punti.
  2. Se la coppia di punti corrente passa esattamente attraverso qualsiasi punto dell'elenco, fermati.
  3. Filtra tutti i punti in senso orario della coppia corrente, poiché renderebbero il poligono concavo.
  4. Per tutti i punti rimasti, procedi come segue:
    1. Se una linea da questo punto al primo punto della catena attraversa o racchiude tutti i punti in senso antiorario, salta questo punto, poiché qualsiasi poligono lo racchiuderebbe.
    2. Aggiungi questo punto alla catena, riprendi dal passaggio 1 con la catena corrente e l'elenco dei punti.
  5. Se non sono rimasti punti e la catena ha almeno 3 punti, questo è un poligono convesso valido. Ricorda la più grande area di questi poligoni.

Inoltre, come ottimizzazione, registriamo la coppia iniziale della catena come selezionata, quindi eventuali ricerche successive vedendo questa coppia in qualsiasi punto della catena possono immediatamente interrompere la ricerca, poiché il poligono più grande con questa coppia è già stato trovato.

Questo algoritmo non dovrebbe mai trovare un poligono due volte e l'ho verificato sperimentalmente.


2
+1, questa è una risposta straordinaria. Potresti essere in grado di sostituire ===e !==con ==e !=, ma non potrei esserne sicuro senza capire il tuo codice ...
jrich

1
Grazie! Quei particolari === e! == stanno confrontando oggetti, quindi no, purtroppo. Ha usato per confrontare gli indici, ma (x,i)=>p.i==i(13 caratteri) è un po 'più lungo di x=>p===x(8 caratteri) anche dopo l'ottimizzazione.
ricochet1k,

2
C'è una spiegazione per te @Lembik
ricochet1k,

1
Sembra che tu abbia battuto il record O (n ^ 3) menzionato nei commenti della domanda SO collegata!

1
Va bene, sto arrivando dove non credo sia possibile che questo funzioni in meno di O (n ^ 3). Sono molto nuovo nella complessità algoritmica.
ricochet1k,
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.