En büyük dışbükey çokgen alanını bulun


28

Tamsayı koordinatların listesi göz önüne alındığında, en büyük alanını bulmak dışbükey çokgen Eğer öyle ki listeden inşa edebilir -

  • Her köşe listede
  • Çokgen içinde listenin hiçbir öğesi yoktur.

Örnek:

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

görsel:

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

Bundan yapabileceğiniz en büyük dışbükey çokgen şudur:

o     
o  o  
 o   o
  o  o
   o
     o

12'lik bir alana sahip.


Koordinat listesini herhangi bir makul formatta alabilir ve ondalık basamağın ardından en az 2 haneye yuvarlanan en büyük dışbükey poligonun alanını (seçtiğiniz dil için uygun bir şekilde) çıkarmalısınız.

Ek olarak, bir çeşit algoritma kullanmalısınız ve basitçe kaba noktaların tüm alt kümelerini zorlamayın. Bunu uygulamak için, programınız modern bir bilgisayarda bir dakikanın altında 50 köşenin listesini çözmelidir.

Bayt cinsinden en kısa kod kazanır.


En kötü durum için hızlı bir algoritma biliyor musunuz?
xnor,

3
Eğer 100 tepe noktasında bir zaman sınırı uygulamak istiyorsanız, muhtemelen en az bir tane böyle bir test senaryosunu dahil etmelisiniz (ideal olarak birkaç, örneğin 100 köşenin hepsinin çözümün bir parçası olduğu, biri 99 ve biri sadece 10'un olduğu) .
Martin Ender

@ MartinBüttner Ne yazık ki, kendimde çalışan bir uygulamam olmadığı için bu test olayını oluşturamıyorum. Sorun oldukça zor :)
orlp

@xnor Burada birkaç örnek bulabilirsiniz .
orlp

"Ondalık noktadan sonra en az 2 basamak yuvarlanır"?
DavidC

Yanıtlar:


12

Javascript ES6, 738 bayt

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

İşte çoğu tarayıcıda ve düğümde tweaking olmadan çalışması gereken bir ES5 veya daha az sürümü: 827 bytes

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

Kod anonim bir işlev döndürür. Parametre olarak, bir dizi noktaya benzer [[0,1],[2,3],[4,5]]. Kullanmak için var f=önce yerleştirebilir veya komut satırından kullanmak isterseniz (process.argv[2].replace(/ /g,'').slice(1,-1).split(')(').map((x)=>x.split(',')))sonuna ekleyin venode convpol.js '(1,2)(3,4)(5,6)'

Mücadele için teşekkürler! Referans uygulaması olmadığı için, bunun doğru olduğunu ispat edemem, ancak en azından puan listesinin izinleri için tutarlıdır. Neredeyse bunun işe yarayacağını düşünmemiştim, çünkü hata ayıklama koduna sahip sürümler, hatta devre dışı bırakılsa bile, üstel zaman artışı ile çok yavaşlardı. Yine de golf oynamaya karar verdim ve makinemde 50 puan için 2 saniyenin altına düştüğünü görmek beni mutlu etti. 1 dakikada yaklaşık 130 puan hesaplayabilir.

Algoritma Graham taramasına benzer , ancak her yerde boş dışbükey gövdeleri aramak zorunda olması dışında.

açıklama

İşte algoritmanın nasıl çalıştığına dair üst düzey bir bakış. Bu algoritmanın eti, sadece bir noktayı içermeyen saat yönünün tersine dışbükey döngüler arar. Prosedür böyle bir şeydir:

  1. Bir çift nokta ve diğer tüm noktaların bir listesiyle başlayın.
  2. Geçerli nokta çifti listedeki herhangi bir noktadan tamamen geçerse, durun.
  3. Poligonu içbükey yapacaklarından, tüm noktaları mevcut çiftin saat yönünde filtreleyin.
  4. Kalan tüm noktalar için aşağıdakileri yapın:
    1. Bu noktadan zincirin ilk noktasına kadar olan bir çizgi saatin tersi yönünde herhangi bir noktayı geçerse ya da çevrelerse, herhangi bir çokgen noktayı içine alacağından bu noktayı atlayın.
    2. Bu noktayı zincire ekleyin, 1. adımdan itibaren geçerli zincir ve noktaların listesiyle tekrarlayın.
  5. Hiçbir nokta kalmamışsa ve zincir en az 3 puana sahipse, bu geçerli bir dışbükey çokgendir. Bu çokgenlerin en geniş alanını hatırla.

Ayrıca, bir optimizasyon olarak, zincirin ilk çiftini kontrol edilmiş olarak kaydederiz, böylece bu çiftin zincirde herhangi bir yerinde görüldükten sonraki aramalar hemen aramayı durdurabilir, çünkü bu çiftin en büyük poligonu zaten bulundu.

Bu algoritma asla iki kez çokgen bulamamalı ve bunu deneysel olarak doğruladım.


2
+1, bu inanılmaz bir cevap. Değiştirebilir ===ve !==ile ==ve mümkün olabilir !=, ancak kodunuzu anlamadan emin
olamadım

1
Teşekkürler! Bu özel === ve! == nesneleri karşılaştırıyor, ne yazık ki hayır. Endeksleri karşılaştırmak için kullanılır, ancak (x,i)=>p.i==i(13 karakter) x=>p===xoptimizasyondan sonra bile (8 karakterden) biraz daha uzundur .
ricochet1k

2
Sizin için bir açıklama var @ Lembik
ricochet1k

1
Bağlantılı SO sorusunun yorumlarında belirtilen O (n ^ 3) kaydını yendin!

1
Tamam, bunun O (n ^ 3) 'den daha az çalışmasının mümkün olabileceğine inanmadığım bir yere gidiyorum. Algoritmik karmaşıklık konusunda çok yeniyim.
ricochet1k,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.