将方钉放入方孔


20

我对《纽约时报》的这张图片的设计很感兴趣,其中每个美国州都由一个正方形的网格表示。我想知道他们是手动放置正方形还是实际上找到了一个最优的正方形放置(在某种定义下)来表示连续状态的位置。

纽约时报的枪支背景检查图

您的代码将只承受一小部分挑战,即以最佳方式放置正方形来表示状态(或其他任意二维形状)。具体地说,它假设我们已经具有所有形状的地理中心或质心一种方便的格式,并且在这样的图中,数据的最佳表示形式是从形状的质心到代表它们的正方形中心的总距离最小,每个中最多一个正方形可能的位置。

您的代码将以任何方便的格式获取从0.0到100.0(含)之间的唯一的浮点X和Y坐标对列表,并将以最佳位置表示数据的网格中输出单位平方的非负整数坐标。 ,保持秩序。如果正方形的多个排列是最佳的,则可以输出任何最佳排列。将给出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

只是为了好玩:

这是我们输入格式下的连续美国地理中心的表示,大致与《纽约时报》使用的比例:

[(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所使用的地图几乎肯定不是最佳的。
路加福音

Answers:


3

Mathematica,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. 找到这些点的成本最低的重新分配到该点的9个正方形的邻域,不包括在步骤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

以及在USA数据上运行它的结果(由于实用程序功能将结果转换为SVG): 连续美国的示意图

这比未编写代码的代码要差一些;唯一可见的区别是,最右上角的正方形在较好的正方形中离左边更远。


您会拍拍背部!看起来我需要对经度进行缩放,以使其看起来更像《纽约时报》上的图表。
路加福音

出于好奇,您为美国地图得到的总距离是多少?
汤姆·卡彭特

我可能应该问过我自己这个问题……因为这只是向我展示了我的高尔夫球运动比我想象的要糟糕。我最初的原始版本是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。

地图

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.