Поклавши квадратні кілочки в квадратні отвори


20

Мене заінтригувало оформлення цієї графіки з New York Times, в якій кожен штат США представлений квадратом у сітці. Мені було цікаво, чи розміщують вони квадрати вручну чи насправді знаходять оптимальне розміщення квадратів (за деяким визначенням) для відображення позицій суміжних станів.

Графік перевірки тла гармати від New York Times

Ваш код буде вирішувати невелику частину завдання оптимального розміщення квадратів для відображення станів (або інших довільних двовимірних фігур.) Зокрема, передбачається, що у нас вже є всі географічні центри або центроїди фігур у зручний формат, і що оптимальне представлення даних на подібній діаграмі - це та, в якій загальна відстань від центроїдів фігур до центрів квадратів, що їх представляють, мінімальна, максимум по одному квадрату в кожному можливе положення.

Ваш код буде містити список унікальних пар координат X і Y з плаваючою комою від 0,0 до 100,0 (включно) у будь-якому зручному форматі, і виводить невід’ємні цілі цілі координати одиниць квадратів у сітці, оптимально розміщеній для представлення даних , збереження порядку. У випадках, коли кілька розташувань квадратів є оптимальними, ви можете вивести будь-яку з оптимальних композицій. Буде задано від 1 до 100 пар координат.

Це код гольфу, найкоротший виграш коду.

Приклади:

Вхід: [(0.0, 0.0), (1.0, 1.0), (0.0, 1.0), (1.0, 0.0)]

Це легке. Центри квадратів у нашій сітці знаходяться в межах 0,0, 1,0, 2,0 і т.д., тож ці фігури вже ідеально розміщені в центрах квадратів за цим малюнком:

21
03

Отже, ваш вихід повинен бути саме цими координатами, але як цілі числа, у форматі на ваш вибір:

[(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), але нам їх потрібно відштовхнути, оскільки два квадрати не можуть бути в одному положенні. Мінімізація відстані від центральної форми фігури до центру квадрата, що її представляє, дає нам такий малюнок:

 1
402
 3

Отже, ваш вихід повинен бути [(2, 2), (2, 3), (3, 2), (2, 1), (1, 2)].

Тестові приклади:

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

Загальна відстань від центроїдів фігур до центрів квадратів, які представляють їх у кожному випадку (будь ласка, повідомте мене, якщо ви помітили якісь помилки!):

0.0
3.6
4.087011
13.243299
2.724791
1.144123

Задля розваги:

Ось представлення географічних центрів сусідніх Сполучених Штатів у нашому форматі введення, приблизно в масштабі, який використовував 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)]

Для їх отримання я взяв координати з другого списку на цій сторінці і використав 0.4 * (125.0 - longitude)для нашої X координати та 0.4 * (latitude - 25.0)для нашої Y координати. Ось як виглядає сюжет:

Ділянка географічних центрів сусідніх Сполучених Штатів.

Перша особа, яка використовує вихід із свого коду з вказаними вище координатами як вхідні дані для створення діаграми з фактичними квадратами, отримує погладжування на звороті!


Я вважаю, останнім моментом у вашому другому прикладі має бути (1, 2), ні (1, 1).
Тім Педерік

Гарний улов, дякую!
Лука

Чи можете ви також розмістити загальну відстань у кожному тестовому випадку? Це, звичайно, нетривіальна проблема, і це дозволить нам перевірити, чи альтернативне рішення насправді також є оптимальним.
недолік

PS: Ви фактично перевіряли, що дана карта є дійсно вагомим результатом вашої проблеми оптимізації? Тому що інтуїтивно я не думаю, що це так.
недолік

Я можу додати загальні відстані. Карта, яку використовував Times, майже точно не є оптимальною.
Лука

Відповіді:


3

Математика, 473 байт

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)

Перед гольфом:

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)

Пояснення :

Цю проблему оптимізації важко описати в Mathematica. Дано список точок pдовжини n,

  • змінні x[i]і y[i]: v=Array[{x[#],y[#]}&,n],
  • функція , щоб мінімізувати це сума зсувів: f=Total[Norm/@(p-v)],
  • обмеження є: c=Flatten[v]∈Integers&&And@@(Or@@Thread[#1!=#2]&@@@Subsets[v,{2}]).

І, NMinimize[{f,cons},v,MaxIterations->Infinity]дасть результат. Але, на жаль, така схема прямого руху здається занадто складною для сходження.

Щоб вирішити проблему складності, прийняті дві методики:

  • велика "взаємодія",, If[#1==#2,1*^4,0]&використовується для уникнення зіткнення між точками.
  • замість того, щоб оптимізувати всі змінні одночасно, ми оптимізуємо кожну точку з їх сусідами по черзі.

Ми починаємо з початкової здогадки, округляючи точки. Коли оптимізація проводиться одна за одною, очікується, що зіткнення будуть вирішені, а також буде встановлено оптимізоване розташування.

Остаточне рішення є принаймні хорошим, якщо не оптимальним. (Я вірю :P)


Результат :

Результат " Просто заради розваги" показаний нижче. Темно-зелені точки - це входи, сірі квадрати - виходи, а чорні лінії показують зміщення.

введіть тут опис зображення

Сума переміщень становить 19,4595 . І рішення є

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

Га! Я тільки думав скласти таку схему, як останній. Молодці.
Тім Педерік

Хороша робота. Інтуїтивно, ваше рішення на карті США мені здається оптимальним.
Лука

2

Python 3, 877 байт

Це не правильна реалізація. Він виходить з ладу у другому з "подальших тестових випадків", виробляючи рішення із загальною відстані 13,5325, де для поданого рішення потрібно лише 13,2433. Ще більше ускладнює питання той факт, що моя реалізація гольфу не відповідає тому, що я не написав, що я написав першим ...

Однак ніхто інший не відповів, і це занадто цікавий виклик, щоб пропустити повз. Також у мене є зображення, сформоване з даних США, тож ось це.

Алгоритм приблизно такий:

  1. Натисніть всі точки до найближчих цілих координат (надалі їх називають «квадратом»).
  2. Знайдіть квадрат із найбільшою кількістю точок.
  3. Знайдіть перерозподіл цих точок з найнижчою вартістю на дев'ять квадратних околиць цього, виключаючи будь-які квадрати, які вже були оброблені на кроці 2.
    • Перерозподіл обмежений однією точкою на квадрат, якщо тільки це не забезпечить достатню кількість квадратів (хоча навіть тоді на цьому квадраті залишиться лише одна точка ).
  4. Повторюйте з кроку 2, поки жоден квадрат не має більше однієї точки.
  5. Знайдіть кожну з вихідних точок за порядком та виведіть їхні квадрати в порядку.

Я не маю абсолютно ніяких доказів оптимальності для будь-якої частини цього алгоритму, просто сильна підозра, що він дасть "досить хороші" результати. Я думаю, що це те, що ми називали "евристичним алгоритмом" ще в мої дні університету ...!

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

І результат його запуску за даними США (завдяки функції утиліти, яка перетворює результати у SVG): Схематична карта суміжних Сполучених Штатів

Це дещо гірше, ніж той, який виробляв незворушний код; Єдина видима відмінність полягає в тому, що крайній правий квадрат знаходиться один далі зліва в кращому.


Ви отримуєте погладжування по спині! Схоже, мені потрібно попрацювати над масштабуванням довготи, щоб зробити це трохи більше схожим на діаграму з Times.
Лука

З цікавості, яку загальну відстань ви отримаєте для вас на карті США?
Том Карпентер

Напевно, я мав би задати собі це питання ... тому що мені просто показано, що моя реалізація гольфу гірша, ніж я думав. У моїй оригінальній версії, яка не перебуває у вогонь, вона отримала її в 20.9164, але версія, яку я розмістив, дала мені 20.9987 * зітхання *
Тім Педерік

1

MATLAB, 316 343 326 байт

Це - незавершена робота - вона не швидка, але коротка. Здається, проходить більшість тестових випадків. Наразі працює лише те, що цікаво для введення карти, але воно все ще триває через 10 хвилин, тож ...

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

І в дещо більш читаному форматі:

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

Очікується, що формат введення буде масивом MATLAB, таким як:

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

Що досить близьке до формату у питанні, що дозволяє деяку свободу.

Вихід є в тому ж форматі, що і вхід, масив, де будь-який даний індекс відповідає одній і тій же точці як вводу, так і виходу.


Хм, 8 годин і все ще працює на карті ... це рішення гарантовано знайде найоптимальніший, але це робиться через грубу силу, тому потрібно дуже багато часу.

Я придумав інше рішення, яке набагато швидше, але, як і інша відповідь, не вдалося знайти найбільш оптимальне в одному з тестових випадків. Цікаво, що карта, яку я отримую для свого іншого рішення (не розміщеного), показана нижче. Він досягає загальної відстані 20,72.

Карта

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.