Trouvez l'aire du plus grand polygone convexe

29

Étant donné une liste de coordonnées entières, trouvez l'aire du plus grand polygone convexe que vous pouvez construire à partir de la liste de telle sorte que -

  • chaque sommet est dans la liste
  • aucun élément de la liste n'est contenu dans le polygone.

Exemple:

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

Visualisé:

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

Le plus grand polygone convexe que vous pouvez créer est le suivant:

o     
o  o  
 o   o
  o  o
   o
     o

D'une superficie de 12.


Vous pouvez prendre la liste des coordonnées dans n'importe quel format raisonnable et devez afficher (d'une manière appropriée pour la langue de votre choix) la zone du plus grand polygone convexe, arrondie à pas moins de 2 chiffres après la virgule décimale.

De plus, vous devez utiliser une sorte d'algorithme, et pas simplement forcer brutalement tous les sous-ensembles de points. Pour appliquer cela, votre programme doit résoudre une liste de 50 sommets en moins d'une minute sur un PC moderne.

Le code le plus court en octets gagne.

orlp
la source
Connaissez-vous un algorithme rapide pour le pire des cas?
xnor
3
Si vous souhaitez appliquer une limite de temps à 100 sommets, vous devez probablement inclure au moins un tel cas de test (idéalement plusieurs, par exemple un où tous les 100 sommets font partie de la solution, un où 99 le sont et un où seuls 10 le sont) .
Martin Ender
@ MartinBüttner Malheureusement, je ne peux pas générer ce cas de test car je n'ai pas moi-même d'implémentation fonctionnelle. Le problème est plutôt délicat :)
orlp
@xnor Quelques exemples peuvent être trouvés ici .
orlp
"arrondi à pas moins de 2 chiffres après la virgule"?
DavidC

Réponses:

12

Javascript ES6, 738 octets

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

Voici une version ES5 ou moins qui devrait fonctionner dans la plupart des navigateurs et des nœuds sans peaufiner: 827 octets

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

Le code renvoie une fonction anonyme. En tant que paramètre, il prend un tableau de points, comme [[0,1],[2,3],[4,5]]. Pour l'utiliser, vous pouvez le placer var f=avant, ou si vous souhaitez l'utiliser à partir de la ligne de commande, ajoutez (process.argv[2].replace(/ /g,'').slice(1,-1).split(')(').map((x)=>x.split(',')))à la fin et appelez-le commenode convpol.js '(1,2)(3,4)(5,6)'

Merci pour le défi! Comme il n'y a pas d'implémentation de référence, je ne peux pas prouver que c'est correct, mais c'est cohérent au moins pour les permutations de la liste de points. Je ne pensais presque pas que cela allait fonctionner, car les versions avec le code de débogage, même désactivées, étaient beaucoup trop lentes avec une augmentation exponentielle du temps. J'ai quand même décidé de jouer au golf et j'étais ravi de voir qu'il était tombé à moins de 2 secondes pour 50 points sur ma machine. Il peut calculer environ 130 points en 1 minute.

L'algorithme est similaire au balayage Graham , sauf qu'il doit rechercher des coques convexes vides partout.

Explication

Voici un aperçu de haut niveau du fonctionnement de l'algorithme. La viande de cet algorithme recherche simplement des boucles convexes dans le sens antihoraire qui ne renferment pas de point. La procédure ressemble à ceci:

  1. Commencez avec une paire de points et une liste de tous les autres points.
  2. Si la paire de points actuelle passe exactement par n'importe quel point de la liste, arrêtez.
  3. Filtrez tous les points dans le sens horaire de la paire actuelle, car ils rendraient le polygone concave.
  4. Pour tous les points restants, procédez comme suit:
    1. Si une ligne allant de ce point au premier point de la chaîne passe par ou englobe des points dans le sens inverse des aiguilles d'une montre, sautez ce point, car tous les polygones entoureraient le point.
    2. Ajoutez ce point à la chaîne, reprenez à partir de l'étape 1 avec la chaîne actuelle et la liste des points.
  5. S'il n'y avait plus de points et que la chaîne a au moins 3 points, c'est un polygone convexe valide. N'oubliez pas la plus grande zone de ces polygones.

De plus, à titre d'optimisation, nous enregistrons la paire initiale de la chaîne comme vérifiée, de sorte que toute recherche ultérieure après avoir vu cette paire n'importe où dans la chaîne peut immédiatement arrêter la recherche, car le plus grand polygone avec cette paire a déjà été trouvé.

Cet algorithme ne devrait jamais trouver un polygone deux fois, et je l'ai vérifié expérimentalement.

ricochet1k
la source
2
+1, c'est une réponse étonnante. Vous pourriez être en mesure de remplacer ===et !==avec ==et !=, mais je ne pouvais pas être sûr sans comprendre votre code ...
jrich
1
Merci! Ces === et! == particuliers comparent les objets, donc non, malheureusement. Il comparait les indices, mais (x,i)=>p.i==i(13 caractères) est un peu plus long que x=>p===x(8 caractères) même après optimisation.
ricochet1k
2
Il y a une explication pour vous @Lembik
ricochet1k
1
Vous semblez avoir battu le record O (n ^ 3) mentionné dans les commentaires de la question SO liée!
1
D'accord, j'arrive là où je ne pense pas qu'il soit possible que cela fonctionne en moins de O (n ^ 3). Je suis très nouveau dans la complexité algorithmique.
ricochet1k