Đặt chốt vuông vào lỗ vuông


20

Tôi bị thu hút bởi thiết kế của đồ họa này từ Thời báo New York, trong đó mỗi tiểu bang Hoa Kỳ được đại diện bởi một hình vuông trong một lưới. Tôi tự hỏi liệu họ đặt các hình vuông bằng tay hay thực sự tìm thấy một vị trí tối ưu của hình vuông (theo một số định nghĩa) để đại diện cho vị trí của các trạng thái tiếp giáp.

Đồ họa kiểm tra súng từ New York Times

Mã của bạn sẽ đảm nhận một phần nhỏ trong thử thách đặt tối ưu các hình vuông để thể hiện các trạng thái (hoặc các hình dạng hai chiều tùy ý khác.) Cụ thể, chúng tôi sẽ giả định rằng chúng tôi đã có tất cả các trung tâm địa lý hoặc tâm của hình dạng trong một định dạng thuận tiện và biểu diễn tối ưu của dữ liệu trong sơ đồ như thế này là một khoảng cách trong đó tổng khoảng cách từ tâm của các hình đến tâm của các hình vuông đại diện cho chúng là tối thiểu, với tối đa một ô vuông trong mỗi sơ đồ vị trí có thể.

Mã của bạn sẽ lấy danh sách các cặp tọa độ X và Y dấu phẩy động duy nhất từ ​​0,0 đến 100,0 (bao gồm) ở bất kỳ định dạng thuận tiện nào và sẽ xuất ra tọa độ số nguyên không âm của các ô vuông đơn vị trong lưới được đặt tối ưu để biểu thị dữ liệu , giữ gìn trật tự. Trong trường hợp nhiều cách sắp xếp hình vuông là tối ưu, bạn có thể xuất ra bất kỳ sự sắp xếp tối ưu nào. Từ 1 đến 100 cặp tọa độ sẽ được đưa ra.

Đây là mã golf, mã ngắn nhất thắng.

Ví dụ:

Đầu vào: [(0.0, 0.0), (1.0, 1.0), (0.0, 1.0), (1.0, 0.0)]

Đây là một điều dễ dàng. Các tâm của hình vuông trong lưới của chúng tôi nằm ở 0,0, 1,0, 2.0, v.v., vì vậy những hình này đã được đặt hoàn hảo ở trung tâm của hình vuông trong mẫu này:

21
03

Vì vậy, đầu ra của bạn phải chính xác là các tọa độ này, nhưng là số nguyên, theo định dạng bạn chọn:

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

Đầu vào: [(2.0, 2.1), (2.0, 2.2), (2.1, 2.0), (2.0, 1.9), (1.9, 2.0)]

Trong trường hợp này, tất cả các hình dạng đều nằm gần tâm của hình vuông tại (2, 2), nhưng chúng ta cần đẩy chúng ra xa vì hai hình vuông không thể ở cùng một vị trí. Giảm thiểu khoảng cách từ tâm của hình đến tâm hình vuông thể hiện nó cho chúng ta mẫu này:

 1
402
 3

Vì vậy, đầu ra của bạn nên được [(2, 2), (2, 3), (3, 2), (2, 1), (1, 2)].

Các trường hợp thử nghiệm:

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

Tổng khoảng cách từ tâm của hình đến tâm của các hình vuông đại diện cho chúng trong từng trường hợp (vui lòng cho tôi biết nếu bạn phát hiện bất kỳ lỗi nào!):

0.0
3.6
4.087011
13.243299
2.724791
1.144123

Chỉ để cho vui:

Đây là một đại diện của các trung tâm địa lý của Hoa Kỳ liền kề ở định dạng đầu vào của chúng tôi, với quy mô gần bằng thời gian mà Times sử dụng:

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

Để có được những điều này, tôi lấy tọa độ từ danh sách thứ hai trên trang này và sử dụng 0.4 * (125.0 - longitude)cho tọa độ X của chúng tôi và 0.4 * (latitude - 25.0)cho tọa độ Y của chúng tôi. Đây là những gì trông giống như âm mưu:

Âm mưu của các trung tâm địa lý của Hoa Kỳ tiếp giáp.

Người đầu tiên sử dụng đầu ra từ mã của họ với tọa độ trên làm đầu vào để tạo một sơ đồ với các ô vuông thực tế được vỗ nhẹ vào mặt sau!


Tôi tin rằng điểm cuối cùng trong ví dụ thứ hai của bạn là (1, 2), không (1, 1).
Tim Pederick

Bắt tốt, cảm ơn!
Lu-ca

Bạn cũng có thể xin vui lòng gửi tổng số khoảng cách trong mỗi trường hợp thử nghiệm? Đây chắc chắn là một vấn đề không cần thiết, và điều đó sẽ cho phép chúng tôi xác minh xem một giải pháp thay thế có thực sự tối ưu hay không.
flawr

PS: Bạn đã thực sự kiểm tra rằng bản đồ đã cho có thực sự là kết quả hợp lệ của vấn đề tối ưu hóa của bạn không? Bởi vì trực giác tôi không nghĩ rằng nó là.
flawr

Tôi có thể thêm tổng khoảng cách. Bản đồ mà Times sử dụng gần như chắc chắn không tối ưu.
Lu-ca

Câu trả lời:


3

Toán học, 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)

Trước khi chơi 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)

Giải thích :

Vấn đề tối ưu hóa này không khó để mô tả trong Mathematica. Đưa ra một danh sách các điểm pcó độ dài n,

  • các biến là x[i]y[i]: v=Array[{x[#],y[#]}&,n],
  • chức năng để giảm thiểu là tổng số chuyển vị : f=Total[Norm/@(p-v)],
  • các ràng buộc là : c=Flatten[v]∈Integers&&And@@(Or@@Thread[#1!=#2]&@@@Subsets[v,{2}]).

Và, NMinimize[{f,cons},v,MaxIterations->Infinity]sẽ cho kết quả. Nhưng thật không may, sơ đồ chuyển tiếp thẳng như vậy dường như quá phức tạp để hội tụ.

Để giải quyết vấn đề phức tạp, hai kỹ thuật được áp dụng:

  • một "tương tác" lớn If[#1==#2,1*^4,0]&, được sử dụng để tránh va chạm giữa các điểm.
  • thay vì tối ưu hóa tất cả các biến cùng một lúc, chúng tôi lần lượt tối ưu hóa trên mọi điểm với hàng xóm của chúng.

Chúng tôi bắt đầu từ một dự đoán ban đầu bằng cách làm tròn các điểm. Khi tối ưu hóa được thực hiện từng cái một, các va chạm dự kiến ​​sẽ được giải quyết và một sự sắp xếp tối ưu hóa được thiết lập.

Giải pháp cuối cùng ít nhất là tốt, nếu không tối ưu. (Tôi tin :P)


Kết quả :

Kết quả của Just for fun được hiển thị bên dưới. Điểm màu xanh đậm là đầu vào, hình vuông màu xám là đầu ra và đường màu đen hiển thị các chuyển vị.

nhập mô tả hình ảnh ở đây

Tổng số chuyển vị là 19,4595 . Và giải pháp là

{{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}}

Hà! Tôi chỉ nghĩ đến việc tạo ra một sơ đồ như thế cuối cùng. Làm tốt.
Tim Pederick

Làm tốt lắm. Theo trực giác, giải pháp của bạn cho bản đồ của Hoa Kỳ có vẻ tối ưu với tôi.
Luke

2

Python 3, 877 byte

Đây không phải là một thực hiện chính xác. Nó đã thất bại ở lần thứ hai trong "các trường hợp thử nghiệm tiếp theo", tạo ra một giải pháp với tổng khoảng cách là 13,5325, trong đó giải pháp được cung cấp chỉ cần 13,2433. Vấn đề phức tạp hơn nữa là thực tế là việc thực hiện đánh gôn của tôi không phù hợp với điều chưa được tôi viết trước ...

Tuy nhiên, không ai khác đã trả lời, và đây là một thử thách quá thú vị để vượt qua. Ngoài ra, tôi có một hình ảnh được tạo từ dữ liệu của Hoa Kỳ, vì vậy có.

Thuật toán là một cái gì đó như thế này:

  1. Đẩy tất cả các điểm đến tọa độ nguyên gần nhất (sau đây gọi là "hình vuông").
  2. Tìm hình vuông có số điểm lớn nhất.
  3. Tìm phân phối lại chi phí thấp nhất của các điểm đó cho vùng lân cận chín ô vuông của điểm này, ngoại trừ bất kỳ hình vuông nào đã được xử lý trong bước 2.
    • Việc phân phối lại được giới hạn ở một điểm trên mỗi ô vuông, trừ khi điều đó không cung cấp đủ hình vuông (mặc dù sau đó, chỉ có một điểm sẽ vẫn còn trên hình vuông này ).
  4. Lặp lại từ bước 2 cho đến khi không có hình vuông nào có nhiều hơn một điểm.
  5. Xác định vị trí từng điểm ban đầu, theo thứ tự và xuất các ô vuông của chúng theo thứ tự.

Tôi hoàn toàn không có bằng chứng về sự tối ưu cho bất kỳ phần nào của thuật toán này, chỉ có một sự nghi ngờ mạnh mẽ rằng nó sẽ cung cấp kết quả "khá tốt". Tôi nghĩ đó là cái mà chúng tôi gọi là "thuật toán heuristic" trong thời đại uni của tôi ...!

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

Và kết quả của việc chạy nó trên dữ liệu của Hoa Kỳ (nhờ một chức năng tiện ích biến kết quả thành SVG): Một bản đồ sơ đồ của Hoa Kỳ tiếp giáp

Điều này hơi tệ hơn so với mã không được tạo ra; sự khác biệt duy nhất có thể nhìn thấy là hình vuông trên cùng bên phải là một hình nữa ở bên trái trong hình vuông tốt hơn.


Bạn nhận được một cái vỗ nhẹ vào lưng! Có vẻ như tôi cần phải làm việc theo tỷ lệ kinh độ để làm cho nó trông giống một chút với sơ đồ từ Times.
Luke

Vì tò mò, bạn có bao nhiêu khoảng cách cho bản đồ Hoa Kỳ?
Tom Carpenter

Có lẽ tôi nên hỏi câu hỏi đó của chính mình ... bởi vì nó chỉ cho tôi thấy rằng việc thực hiện chơi gôn của tôi tệ hơn tôi nghĩ. Phiên bản gốc, không có bản quyền của tôi đã nhận được vào năm 20.9164, nhưng phiên bản tôi đã đăng cho tôi 20,9987. * thở dài *
Tim Pederick

1

MATLAB, 316 343 326 byte

Đây là một công việc đang tiến triển - nó không nhanh, nhưng nó ngắn. Nó dường như vượt qua hầu hết các trường hợp thử nghiệm. Hiện tại, ứng dụng chỉ dành cho mục nhập thú vị của bản đồ đang chạy, nhưng nó vẫn hoạt động sau 10 phút, vì vậy ...

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

Và trong một định dạng dễ đọc hơn:

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

Định dạng đầu vào dự kiến ​​là một mảng MATLAB, chẳng hạn như:

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

Điều này khá gần với định dạng trong câu hỏi, cho phép một số chậm trễ.

Đầu ra có cùng định dạng với đầu vào, một mảng trong đó bất kỳ chỉ mục đã cho nào cũng tương ứng với cùng một điểm trong cả đầu vào và đầu ra.


Hmm, 8 giờ và vẫn đang chạy trên bản đồ một ... giải pháp này được đảm bảo để tìm ra tối ưu nhất, nhưng nó thực hiện thông qua lực lượng vũ phu, vì vậy mất một thời gian rất dài.

Tôi đã đưa ra một giải pháp khác nhanh hơn nhiều, nhưng giống như câu trả lời khác không tìm thấy tối ưu nhất trong một trong các trường hợp thử nghiệm. Điều thú vị là bản đồ tôi nhận được cho giải pháp khác của tôi (không được đăng) được hiển thị bên dưới. Nó đạt được tổng khoảng cách là 20,72.

Bản đồ

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.