Menempatkan pasak persegi ke dalam lubang persegi

20

Saya tertarik dengan desain grafik ini dari New York Times, di mana setiap negara bagian AS diwakili oleh sebuah kotak di kotak. Saya bertanya-tanya apakah mereka menempatkan kotak dengan tangan atau benar-benar menemukan penempatan kotak yang optimal (berdasarkan beberapa definisi) untuk mewakili posisi negara bagian yang berdekatan.

Latar belakang pistol memeriksa grafik dari New York Times

Kode Anda akan mengambil sebagian kecil dari tantangan penempatan kuadrat secara optimal untuk mewakili negara (atau bentuk dua dimensi sewenang-wenang lainnya.) Secara khusus, ini akan mengasumsikan bahwa kami telah memiliki semua pusat geografis atau centroid dari bentuk di format yang nyaman, dan bahwa representasi optimal dari data dalam diagram seperti ini adalah yang di mana total jarak dari centroid bentuk ke pusat kotak yang mewakili mereka minimal, dengan paling banyak satu kotak di setiap posisi yang mungkin.

Kode Anda akan mengambil daftar pasangan unik titik koordinat X dan Y mulai 0,0 hingga 100,0 (inklusif) dalam format apa pun yang nyaman, dan akan menampilkan koordinat bilangan bulat non-negatif dari kuadrat unit dalam kisi yang ditempatkan secara optimal untuk mewakili data , menjaga ketertiban. Dalam kasus di mana beberapa pengaturan kuadrat optimal, Anda dapat menampilkan salah satu pengaturan optimal. Antara 1 dan 100 pasang koordinat akan diberikan.

Ini golf kode, kode terpendek menang.

Contoh:

Memasukkan: [(0.0, 0.0), (1.0, 1.0), (0.0, 1.0), (1.0, 0.0)]

Ini mudah. Pusat-pusat kotak di kotak kami berada di 0,0, 1.0, 2.0, dll. Jadi bentuk-bentuk ini sudah ditempatkan dengan sempurna di pusat-pusat kotak dalam pola ini:

21
03

Jadi output Anda harus persis koordinat ini, tetapi sebagai bilangan bulat, dalam format pilihan Anda:

[(0, 0), (1, 1), (0, 1), (1, 0)]

Memasukkan: [(2.0, 2.1), (2.0, 2.2), (2.1, 2.0), (2.0, 1.9), (1.9, 2.0)]

Dalam hal ini semua bentuk berada dekat dengan pusat kotak di (2, 2), tetapi kita perlu mendorongnya menjauh karena dua kotak tidak dapat berada di posisi yang sama. Meminimalkan jarak dari centroid bentuk ke pusat kotak yang melambangkannya memberi kita pola ini:

 1
402
 3

Jadi output Anda seharusnya [(2, 2), (2, 3), (3, 2), (2, 1), (1, 2)].

Kasus uji:

[(0.0, 0.0), (1.0, 1.0), (0.0, 1.0), (1.0, 0.0)] -> [(0, 0), (1, 1), (0, 1), (1, 0)]
[(2.0, 2.1), (2.0, 2.2), (2.1, 2.0), (2.0, 1.9), (1.9, 2.0)] -> [(2, 2), (2, 3), (3, 2), (2, 1), (1, 2)]
[(94.838, 63.634), (97.533, 1.047), (71.954, 18.17), (74.493, 30.886), (19.453, 20.396), (54.752, 56.791), (79.753, 68.383), (15.794, 25.801), (81.689, 95.885), (27.528, 71.253)] -> [(95, 64), (98, 1), (72, 18), (74, 31), (19, 20), (55, 57), (80, 68), (16, 26), (82, 96), (28, 71)]
[(0.0, 0.0), (0.1, 0.0), (0.2, 0.0), (0.0, 0.1), (0.1, 0.1), (0.2, 0.1), (0.0, 0.2), (0.1, 0.2), (0.2, 0.2)] -> [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)]
[(1.0, 0.0), (1.0, 0.1), (1.0, 0.2), (1.0, 0.3)] -> [(1, 0), (0, 0), (2, 0), (1, 1)] or [(1, 0), (2, 0), (0, 0), (1, 1)]
[(3.75, 3.75), (4.25, 4.25)] -> [(3, 4), (4, 4)] or [(4, 3), (4, 4)] or [(4, 4), (4, 5)] or [(4, 4), (5, 4)]

Total jarak dari centroid bentuk ke pusat kotak yang mewakili mereka dalam setiap kasus (beri tahu kami jika Anda menemukan kesalahan!):

0.0
3.6
4.087011
13.243299
2.724791
1.144123

Hanya untuk bersenang-senang:

Berikut adalah representasi dari pusat-pusat geografis Amerika Serikat yang berdekatan dalam format input kami, dengan skala kira-kira yang digunakan Times:

[(15.2284, 3.1114), (5.3367, 3.7096), (13.0228, 3.9575), (2.2198, 4.8797), (7.7802, 5.5992), (20.9091, 6.6488), (19.798, 5.5958), (19.1941, 5.564), (17.023, 1.4513), (16.6233, 3.0576), (4.1566, 7.7415), (14.3214, 6.0164), (15.4873, 5.9575), (12.6016, 6.8301), (10.648, 5.398), (15.8792, 5.0144), (13.2019, 2.4276), (22.3025, 8.1481), (19.2836, 5.622), (21.2767, 6.9038), (15.8354, 7.7384), (12.2782, 8.5124), (14.1328, 3.094), (13.0172, 5.3427), (6.142, 8.8211), (10.0813, 6.6157), (3.3493, 5.7322), (21.3673, 7.4722), (20.1307, 6.0763), (7.5549, 3.7626), (19.7895, 7.1817), (18.2458, 4.2232), (9.813, 8.98), (16.8825, 6.1145), (11.0023, 4.2364), (1.7753, 7.5734), (18.8806, 6.3514), (21.3775, 6.6705), (17.6417, 3.5668), (9.9087, 7.7778), (15.4598, 4.3442), (10.2685, 2.5916), (5.3326, 5.7223), (20.9335, 7.6275), (18.4588, 5.0092), (1.8198, 8.9529), (17.7508, 5.4564), (14.0024, 7.8497), (6.9789, 7.1984)]

Untuk mendapatkan ini, saya mengambil koordinat dari daftar kedua di halaman ini dan digunakan 0.4 * (125.0 - longitude)untuk koordinat X kami dan 0.4 * (latitude - 25.0)untuk koordinat Y kami. Inilah yang terlihat seperti diplot:

Plot pusat geografis Amerika Serikat yang berdekatan.

Orang pertama yang menggunakan output dari kode mereka dengan koordinat di atas sebagai input untuk membuat diagram dengan kotak aktual mendapat tepukan di belakang!

Luke
sumber
Saya percaya poin terakhir dalam contoh kedua Anda seharusnya (1, 2), bukan (1, 1).
Tim Pederick
Tangkapan yang bagus, terima kasih!
Luke
Bisakah Anda juga memposting total semua jarak di setiap test case? Ini tentu saja merupakan masalah nontrivial, dan itu akan memungkinkan kami untuk memverifikasi apakah solusi alternatif sebenarnya juga optimal.
flawr
PS: Sudahkah Anda benar-benar menguji bahwa peta yang diberikan adalah hasil valid dari masalah optimasi Anda? Karena secara intuitif saya tidak berpikir demikian.
flawr
Saya dapat menambahkan jarak total. Peta Times digunakan hampir pasti tidak optimal.
Luke

Jawaban:

3

Mathematica, 473 byte

f@p_:=(s=Flatten@Round@p;v=Array[{x@#,y@#}&,n=Length@p];
  Do[w=Flatten[{g@#,h@#}&/@(b=Flatten@Position[p,x_/;Norm[x-p[[i]]]<=2,{1}])];f=Total[Norm/@(p-v)]+Total[If[#1==#2,1*^4,0]&@@@v~Subsets~{2}]/.Flatten[{x@#->g@#,y@#->h@#}&@@@w]/.Thread[Flatten@v->s];
    c=w∈Integers&&And@@MapThread[Max[#-2,0]<=#2<=Min[#+2,100]&,{Flatten@p[[b]],w}];s=Flatten@ReplacePart[s~Partition~2,Thread[b->Partition[w/.Last@Quiet@NMinimize[{f,c},w,MaxIterations->300],2]]]
    ,{i,n}]~Do~{2};s~Partition~2)

Sebelum bermain golf:

f[p_]:=(n=Length@p;s=Flatten@Round@p;v=Array[{x[#],y[#]}&,n];
  Do[
    v2=Flatten[{x2[#],y2[#]}&/@(b=Flatten@Position[p,x_/;Norm[x-p[[i]]]<=2,{1}])];
    f2=Total[Norm/@(p-v)]+Total[If[#1==#2,1*^4,0]&@@@Subsets[v,{2}]]/.Flatten[{x[#]->x2[#],y[#]->y2[#]}&@@@v2]/.Thread[Flatten@v->s];
    c2=v2∈Integers&&And@@MapThread[Max[#-2,0]<=#2<=Min[#+2,100]&,{Flatten@p[[b]],v2}];
    s=Flatten@ReplacePart[s~Partition~2,Thread[b->Partition[v2/.Last@Quiet@NMinimize[{f2,c2},v2,MaxIterations->300],2]]];
    ,{i,n}]~Do~{2};
  s~Partition~2)

Penjelasan :

Masalah optimisasi ini tidak sulit dijelaskan dalam Mathematica. Diberikan daftar titik ppanjang n,

  • variabelnya adalah x[i] dan y[i]: v=Array[{x[#],y[#]}&,n],
  • fungsi untuk meminimalkan adalah total perpindahan: f=Total[Norm/@(p-v)],
  • kendalanya adalah: c=Flatten[v]∈Integers&&And@@(Or@@Thread[#1!=#2]&@@@Subsets[v,{2}]) .

Dan, NMinimize[{f,cons},v,MaxIterations->Infinity]akan memberikan hasilnya. Namun sayangnya, skema yang lurus seperti itu tampaknya terlalu rumit untuk bertemu.

Untuk mengatasi masalah kompleksitas, dua teknik diadopsi:

  • "interaksi" besar, If[#1==#2,1*^4,0]& digunakan untuk menghindari tabrakan antar titik.
  • alih-alih mengoptimalkan semua variabel secara bersamaan, kami mengoptimalkan pada setiap titik dengan tetangga mereka secara bergantian.

Kita mulai dari tebakan awal dengan membulatkan poin. Ketika optimasi dilakukan satu per satu, tabrakan diharapkan untuk diselesaikan, dan pengaturan yang optimal dibuat.

Solusi terakhir setidaknya baik, jika tidak optimal. (Saya percaya :P)


Hasil :

Hasil Just for fun ditunjukkan di bawah ini. Poin hijau gelap adalah input, kotak abu-abu adalah output, dan garis hitam menunjukkan perpindahan.

masukkan deskripsi gambar di sini

Jumlah perpindahan adalah 19,4595 . Dan solusinya adalah

{{15,3},{5,4},{13,4},{2,5},{8,6},{21,6},{20,5},{19,5},{17,1},{17,3},{4,8},{14,6},{15,6},{13,7},{11,5},{16,5},{13,2},{22,8},{19,6},{21,7},{16,8},{12,9},{14,3},{13,5},{6,9},{10,7},{3,6},{22,7},{20,6},{8,4},{20,7},{18,4},{10,9},{17,6},{11,4},{2,8},{19,7},{22,6},{18,3},{10,8},{15,4},{10,3},{5,6},{21,8},{18,5},{2,9},{18,6},{14,8},{7,7}}
njpipeorgan
sumber
Ha! Saya hanya berpikir untuk membuat diagram seperti itu yang terakhir. Sudah selesai dilakukan dengan baik.
Tim Pederick
Kerja bagus. Secara intuitif, solusi Anda untuk peta AS terlihat optimal bagi saya.
Luke
2

Python 3, 877 byte

Ini bukan implementasi yang benar. Gagal pada yang kedua dari "kasus uji lebih lanjut", menghasilkan solusi dengan jarak total 13,5325, di mana solusi yang disediakan hanya perlu 13,2433. Masalah rumit lebih lanjut adalah kenyataan bahwa implementasi golf saya tidak cocok dengan yang ungolfed yang saya tulis pertama ...

Namun, tidak ada orang lain yang menjawab, dan ini tantangan yang terlalu menarik untuk dilewatkan begitu saja. Juga, saya punya gambar yang dihasilkan dari data USA, jadi itu dia.

Algoritme adalah sesuatu seperti ini:

  1. Dorong semua titik ke koordinat bilangan bulat terdekat (selanjutnya disebut "kotak").
  2. Temukan kotak dengan jumlah poin terbesar.
  3. Temukan redistribusi biaya terendah dari titik-titik ke lingkungan sembilan-persegi yang satu ini, tidak termasuk kotak yang telah diproses pada langkah 2.
    • Redistribusi terbatas pada satu titik per kuadrat, kecuali jika itu tidak akan memberikan kuadrat yang cukup (meskipun bahkan kemudian, hanya satu titik akan tetap di alun-alun ini ).
  4. Ulangi dari langkah 2 hingga tidak ada kotak memiliki lebih dari satu titik.
  5. Temukan setiap titik asli, secara berurutan, dan hasilkan kuadratnya, secara berurutan.

Saya sama sekali tidak memiliki bukti optimalitas untuk setiap bagian dari algoritma ini, hanya kecurigaan kuat bahwa itu akan memberikan hasil yang "cukup bagus". Saya pikir itulah yang kami sebut "algoritma heuristik" di masa lalu ...!

l=len
I,G,M=-1,101,150
d=lambda x,y,X,Y:abs(x-X+1j*(y-Y))
N=(0,0),(I,0),(0,I),(1,0),(0,1),(I,I),(1,I),(1,1),(I,I)
n=lambda p,e:[(x,y)for(x,y)in(map(sum,zip(*i))for i in zip([p]*9,N))if(x,y)not in e and I<x<G and I<y<G]
def f(p):
 g={};F=[];O=[I]*l(p)
 for P in p:
  z=*map(round,P),
  if z in g:g[z]+=[P]
  else:g[z]=[P]
 while l(g)<l(p):
  L,*P=0,
  for G in g:
   if l(g[G])>l(P):L,P=G,g[G]
  o=n(L,F);h=l(o)<l(P);c=[[d(*q,*r)for r in o]for q in P];r={}
  while l(r)<l(c):
   A=B=C=M;R=S=0
   while R<l(c):
    if R not in r:
     z=min(c[R])
     if z<A:B,A=R,z;C=c[R].index(A)
    R+=1
   while S<l(c):
    if S==B:
     v=0
     while v<l(c[S]):
      if v!=C:c[S][v]=M
      v+=1
    elif C<1or not h:c[S][C]=M
    S+=1
   r[B]=C
  for q in r:
   x,y=P[q],o[r[q]]
   if y==L or y not in g:g[y]=[x]
   else:g[y]+=[x]
  F+=[L]
 for G in g:
  O[p.index(g[G][0])]=G
 return O

Dan hasil menjalankannya pada data USA (berkat fungsi utilitas yang mengubah hasilnya menjadi SVG): Peta skematis Amerika Serikat yang berdekatan

Ini sedikit lebih buruk daripada yang dihasilkan kode unungolfed; satu-satunya perbedaan yang terlihat adalah bahwa kotak paling kanan atas adalah satu lebih jauh ke kiri di yang lebih baik.

Tim Pederick
sumber
Anda mendapatkan tepukan di punggung! Sepertinya saya perlu bekerja pada penskalaan garis bujur untuk membuatnya terlihat sedikit lebih seperti diagram dari Times.
Luke
Karena penasaran, berapa jarak total yang Anda dapatkan untuk peta USA Anda?
Tom Carpenter
Saya mungkin seharusnya mengajukan pertanyaan itu pada diri saya sendiri ... karena itu hanya menunjukkan kepada saya bahwa implementasi golf saya lebih buruk daripada yang saya kira. Versi asli saya, ungolfed mendapatkannya di 20.9164, tetapi versi yang saya posting memberi saya 20.9987. * sigh *
Tim Pederick
1

MATLAB, 316 343 326 byte

Yang ini sedang dalam proses - tidak cepat, tapi pendek. Tampaknya melewati sebagian besar kasus uji. Saat ini input yang hanya untuk bersenang-senang dari peta sedang berjalan, tetapi masih berjalan setelah 10 menit, jadi ...

function p=s(a)
c=ceil(a');a=a(:,1)+j*a(:,2);[~,p]=r(a,c,[],Inf);p=[real(p),imag(p)];end
function [o,p]=r(a,c,p,o)
if ~numel(c)
o=sum(abs(p-a));else
x=c(1)+(-1:1);y=c(2)+(-1:1);P=p;
for X=1:3
for Y=1:3
Q=x(X)+j*y(Y);if(x(X)>=0)&(y(Y)>=0)&all(Q~=P)
[O,Q]=r(a,c(:,2:end),[P;Q],o);
if(O<o) o=O;p=Q;disp(o);end
end;end;end;end;end

Dan dalam format yang agak lebih mudah dibaca:

function p=squaremap(a)
%Input format: [2.0, 2.1;2.0, 2.2;2.1, 2.0;2.0, 1.9;1.9, 2.0]

    c=ceil(a'); %Convert each point to the next highest integer centre
    a=a(:,1)+j*a(:,2); %Convert each 2D point into a complex number
    [~,p]=r(a,c,[],Inf); %Recurse!
    p=[real(p),imag(p)];
end

function [o,p]=r(a,c,p,o)
    if ~numel(c) %If we are as deep as we can go
        o=sum(abs(p-a)); %See what our overall distance is
    else
        x=c(1)+(-1:1);y=c(2)+(-1:1); %For each point we try 9 points, essentially a 3x3 square
        P=p;
        for X=1:3;
            for Y=1:3
                %For each point
                Q=x(X)+j*y(Y); %Covert to a complex number
                if(x(X)>=0)&(y(Y)>=0)&all(Q~=P) %If the point is not negative and has not already been used this iteration
                    [O,Q]=r(a,c(:,2:end),[P;Q],o); %Otherwise iterate further
                    if(O<o) o=O;p=Q;end %Keep updating the smallest path and list of points we have found
                end
            end
        end
    end
end

Format input diharapkan menjadi array MATLAB, seperti:

[2.0, 2.1;2.0, 2.2;2.1, 2.0;2.0, 1.9;1.9, 2.0]

Yang cukup dekat dengan format dalam pertanyaan, yang memungkinkan beberapa kelonggaran.

Outputnya dalam format yang sama dengan input, sebuah array di mana setiap indeks yang diberikan sesuai dengan titik yang sama di kedua input dan output.


Hmm, 8 jam dan masih berjalan di peta satu ... solusi ini dijamin untuk menemukan yang paling optimal, tetapi melakukannya melalui kekuatan kasar, sehingga membutuhkan waktu yang sangat lama.

Saya telah menemukan solusi lain yang jauh lebih cepat, tetapi seperti jawaban yang lain gagal menemukan yang paling optimal dalam salah satu kasus uji. Menariknya peta yang saya dapatkan untuk solusi saya yang lain (tidak diposting) ditunjukkan di bawah ini. Ini mencapai jarak total 20,72.

Peta

Tom Carpenter
sumber