Mettere pioli quadrati in fori quadrati


20

Sono stato incuriosito dal design di questa grafica del New York Times, in cui ogni stato degli Stati Uniti è rappresentato da un quadrato in una griglia. Mi chiedevo se posizionassero i quadrati a mano o effettivamente trovassero un posizionamento ottimale dei quadrati (sotto una certa definizione) per rappresentare le posizioni degli stati contigui.

Spari il grafico del controllo dei precedenti dal New York Times

Il tuo codice accetterà una piccola parte della sfida di posizionare in modo ottimale i quadrati per rappresentare gli stati (o altre forme bidimensionali arbitrarie). In particolare, si supporrà che abbiamo già tutti i centri geografici o centroidi delle forme in un formato conveniente e che la rappresentazione ottimale dei dati in un diagramma come questo sia quella in cui la distanza totale dai centroidi delle forme ai centri dei quadrati che li rappresentano è minima, con al massimo un quadrato in ciascuno posizione possibile.

Il tuo codice prenderà un elenco di coppie uniche di coordinate X e Y in virgola mobile da 0,0 a 100,0 (incluso) in qualsiasi formato conveniente e produrrà le coordinate intere non negative dei quadrati delle unità in una griglia posizionata in modo ottimale per rappresentare i dati , preservando l'ordine. Nei casi in cui più disposizioni dei quadrati sono ottimali, è possibile produrre una qualsiasi delle disposizioni ottimali. Verranno fornite da 1 a 100 coppie di coordinate.

Questo è il codice golf, il codice più corto vince.

Esempi:

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

Questo è facile. I centri dei quadrati nella nostra griglia sono a 0.0, 1.0, 2.0, ecc. Quindi queste forme sono già perfettamente posizionate ai centri dei quadrati in questo modello:

21
03

Quindi il tuo output dovrebbe essere esattamente queste coordinate, ma come numeri interi, in un formato a tua scelta:

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

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

In questo caso, tutte le forme sono vicine al centro del quadrato in (2, 2), ma dobbiamo allontanarle perché due quadrati non possono essere nella stessa posizione. Ridurre al minimo la distanza dal centroide di una forma al centro del quadrato che lo rappresenta ci dà questo schema:

 1
402
 3

Quindi il tuo output dovrebbe essere [(2, 2), (2, 3), (3, 2), (2, 1), (1, 2)].

Casi test:

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

Distanza totale dai centroidi delle forme ai centri dei quadrati che li rappresentano in ogni caso (per favore fatemi sapere se individuate degli errori!):

0.0
3.6
4.087011
13.243299
2.724791
1.144123

Solo per divertimento:

Ecco una rappresentazione dei centri geografici degli Stati Uniti contigui nel nostro formato di input, approssimativamente alla scala utilizzata dal 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)]

Per ottenerli, ho preso le coordinate dal secondo elenco in questa pagina e utilizzate 0.4 * (125.0 - longitude)per la nostra coordinata X e 0.4 * (latitude - 25.0)per la coordinata Y. Ecco come appare tracciato:

Trama dei centri geografici degli Stati Uniti contigui.

La prima persona che utilizza l'output del proprio codice con le coordinate sopra come input per creare un diagramma con quadrati effettivi ottiene una pacca sulla spalla!


Credo che l'ultimo punto del tuo secondo esempio dovrebbe essere (1, 2), no (1, 1).
Tim Pederick,

Buona cattura, grazie!
Luca,

Potete per favore pubblicare anche il totale di tutte le distanze in ogni caso di test? Questo è certamente un problema non banale e ciò ci consentirebbe di verificare se una soluzione alternativa sia effettivamente ottimale.
flawr

PS: hai effettivamente verificato che la mappa fornita è in realtà un risultato valido del tuo problema di ottimizzazione? Perché intuitivamente non penso che lo sia.
flawr

Posso aggiungere le distanze totali. La mappa utilizzata dal Times non è quasi certamente ottimale.
Luca,

Risposte:


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)

Prima di giocare a 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)

Spiegazione :

Questo problema di ottimizzazione non è difficile da descrivere in Mathematica. Dato un elenco di punti pdi lunghezza n,

  • le variabili sono x[i]e y[i]: v=Array[{x[#],y[#]}&,n],
  • la funzione di ridurre al minimo è il totale di spostamenti: f=Total[Norm/@(p-v)],
  • i vincoli sono: c=Flatten[v]∈Integers&&And@@(Or@@Thread[#1!=#2]&@@@Subsets[v,{2}]).

E, NMinimize[{f,cons},v,MaxIterations->Infinity]darà il risultato. Ma sfortunatamente, un tale schema semplice sembra troppo complicato per convergere.

Per aggirare il problema della complessità, vengono adottate due tecniche:

  • una grande "interazione", If[#1==#2,1*^4,0]&viene utilizzata per evitare la collisione tra punti.
  • invece di ottimizzare tutte le variabili allo stesso tempo, ottimizziamo su ogni punto con i loro vicini a loro volta.

Partiamo da un'ipotesi iniziale arrotondando i punti. Quando le ottimizzazioni vengono eseguite una ad una, si prevede che le collisioni vengano risolte e viene stabilita una disposizione ottimizzata.

La soluzione finale è almeno buona, se non ottimale. (Credo :P)


Risultato :

Il risultato di Just for fun è mostrato di seguito. I punti verde scuro sono gli ingressi, i quadrati grigi sono le uscite e le linee nere mostrano gli spostamenti.

inserisci qui la descrizione dell'immagine

La somma degli spostamenti è 19.4595 . E la soluzione è

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

Ha! Stavo solo pensando di realizzare un diagramma come quello precedente. Molto bene.
Tim Pederick,

Buon lavoro. Intuitivamente, la tua soluzione per la mappa degli Stati Uniti mi sembra ottimale.
Luca

2

Python 3, 877 byte

Questa non è un'implementazione corretta. Non riesce sul secondo dei "ulteriori casi di test", producendo una soluzione con una distanza totale di 13.5325, dove la soluzione fornita richiede solo 13.2433. Ulteriori complicazioni sono il fatto che la mia implementazione golfistica non corrisponde a quella non golfata che ho scritto per prima ...

Tuttavia, nessun altro ha risposto, e questa è una sfida troppo interessante per lasciarsi sfuggire. Inoltre, ho un'immagine generata dai dati USA, quindi c'è.

L'algoritmo è qualcosa del genere:

  1. Spingere tutti i punti verso le coordinate intere più vicine (di seguito chiamato "quadrato").
  2. Trova il quadrato con il maggior numero di punti.
  3. Trova la ridistribuzione più economica di quei punti nel quartiere di nove quadrati di questo, esclusi tutti i quadrati che sono già stati elaborati nel passaggio 2.
    • La ridistribuzione è limitata a un punto per quadrato, a meno che ciò non fornisca abbastanza quadrati (anche se anche allora, su questo quadrato rimarrà solo un punto ).
  4. Ripetere dal passaggio 2 fino a quando nessun quadrato ha più di un punto.
  5. Individua ciascuno dei punti originali, in ordine, e genera i loro quadrati, in ordine.

Non ho assolutamente alcuna prova di ottimalità per qualsiasi parte di questo algoritmo, solo il forte sospetto che fornirà risultati "abbastanza buoni". Penso che sia quello che abbiamo chiamato un "algoritmo euristico" nei miei giorni uni ...!

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

E il risultato della sua esecuzione sui dati USA (grazie a una funzione di utilità che trasforma i risultati in SVG): Una mappa schematica degli Stati Uniti contigui

Questo è leggermente peggiore di quello prodotto dal codice ungolfed; l'unica differenza visibile è che il quadrato in alto a destra è un ulteriore a sinistra in quello migliore.


Ottieni una pacca sulla spalla! Sembra che debba lavorare sul ridimensionamento della longitudine per renderlo un po 'più simile al diagramma del Times.
Luca,

Per curiosità, quale distanza totale ottieni per la tua mappa USA?
Tom Carpenter,

Probabilmente avrei dovuto farmi questa domanda ... perché mi ha appena mostrato che la mia implementazione golfistica è peggiore di quanto pensassi. La mia versione originale, non rigata, è arrivata nel 20.9164, ma la versione che ho pubblicato mi ha dato 20.9987. * sospiro *
Tim Pederick,

1

MATLAB, 316 343 326 byte

Questo è un work in progress - non è veloce, ma è breve. Sembra passare la maggior parte dei casi di test. Attualmente è in esecuzione quello solo per l'input divertente della mappa, ma continua ancora dopo 10 minuti, quindi ...

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

E in un formato un po 'più leggibile:

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

Il formato di input dovrebbe essere un array MATLAB, ad esempio:

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

Che è abbastanza vicino al formato nella domanda, che consente un certo margine di manovra.

L'output è nello stesso formato dell'input, un array in cui ogni dato indice corrisponde allo stesso punto sia in input che in output.


Hmm, 8 ore e ancora in esecuzione sulla mappa uno ... questa soluzione è garantita per trovare il più ottimale, ma lo fa tramite la forza bruta, quindi richiede molto tempo.

Ho trovato un'altra soluzione che è molto più veloce, ma come l'altra risposta non riesce a trovare la soluzione ottimale in uno dei casi di test. È interessante notare che la mappa che ottengo per l'altra mia soluzione (non pubblicata) è mostrata di seguito. Raggiunge una distanza totale di 20,72.

Carta geografica

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.