Problema "Riempi la griglia"


36

Una sfida con regole semplici ma algoritmi non banali. :-)

Compito

Prendi l'input sotto forma di numeri interi separati da spazio:

N A B S

Dove N è la lunghezza laterale di una matrice quadrata 2D riempita con unica numeri (numeri interi) tra A e B compreso. Per ogni riga e colonna in questa matrice, la somma è sempre la stessa: S. (In altre parole, la matrice è un quadrato semi-magico).

Nota:

Tutti i numeri sono positivi. L'eccezione è A, che può essere 0.

Esempi

Per

3 1 10000 2015

una soluzione valida sarebbe

inserisci qui la descrizione dell'immagine

Per

8 1 300 500

una soluzione valida sarebbe

inserisci qui la descrizione dell'immagine

Produzione

L'output dovrebbe essere una tabella ASCII. Esempio per il primo esempio sopra:

 384  159 1472
1174  499  342
 457 1357  201

Interi allineati a destra riempiti con spazi. La larghezza di ogni colonna è la larghezza del numero intero più grande in quella colonna.

punteggio

Questo è , quindi vince il codice più breve in byte. Si applicano scappatoie standard (in particolare sui built-in per risolvere questo problema). Non devi preoccuparti di input errati o altrimenti impossibili (compresi i numeri negativi). Fornisci un esempio di output nella tua risposta (obbligatorio) per il secondo esempio sopra.


1
Ci è permesso generare numeri casuali tra A e B fino a quando non si sommano correttamente e sono univoci?
lirtosiast

Solo per controllare, A, B, e Npuò essere negativo?
xnor

2
minxomat, non sto dicendo che sia la soluzione migliore che posso trovare, sto dicendo che potrebbe essere la più breve possibile.
lirtosiast

3
@LuisMendo Devi generare un output di esempio in base all'attività. Se riesci a gestirlo durante la tua vita con un approccio a forza bruta, sarei impressionato. :-). Potrei escluderlo, ma sarebbe troppo confuso, poiché la soluzione più popolare (che è GA) comporta ancora casualità. Inoltre, non voglio cambiare le regole quando qualcuno potrebbe già lavorare su una soluzione.
mınxomaτ,

1
@minxomat Tutti e tre i tuoi argomenti sono ottimi punti :-)
Luis Mendo,

Risposte:


19

CJam, 119 91 byte

q~:M;),>:R;(:L{{R{ML)d/-Y#)mr}$L/L<2{{M1$:+-+}%z}*:U:+__O|=R*-}gU{:s_:,:e>f{Se[}}%zSf*N*}M?

Questo è un approccio decisamente deterministico, non deterministico.

Sul mio desktop, il secondo caso di test generalmente termina in meno di 10 minuti.

Il primo caso termina all'istante. Provalo online nell'interprete CJam .

Esecuzione del campione

$ cjam grid.cjam <<< '8 1 300 500'
77  66  37 47  56  46 86  85
63 102  70 72  49  54 81   9
62  69  58 57  71  17 48 118
64  65  67 87  53  44 80  40
73  60  55 89  51  76 84  12
68  59  28 78  74  38 50 105
61  75  52 43 125  83 42  19
32   4 133 27  21 142 29 112

Idea

Senza limiti di tempo, potremmo semplicemente generare casualmente quadrati fino a trovare un quadrato valido. Questo approccio si basa su quell'idea, aggiungendo due ottimizzazioni:

  • Invece di generare un quadrato pseudo-casuale di lunghezza laterale N , generiamo quadrati di lunghezza laterale N-1 , aggiungiamo una colonna per formare un rettangolo N × (N-1) le cui righe hanno la somma S , quindi una riga per formare un quadrato di lunghezza del lato N cui colonne sono somma S .

    Poiché la somma degli elementi di tutte le colonne sarà NS e la somma degli elementi dei primi N-1 righe è (N-1) S , l'ultima riga avrà anche somma S .

    Tuttavia, questo processo può generare una matrice non valida, poiché non esiste alcuna garanzia che tutti gli elementi dell'ultima riga e colonna siano univoci o rientrino nell'intervallo [A ... B] .

  • Scegliere un quadrato di numeri interi univoci in [A ... B] e la lunghezza laterale N-1 uniformemente a caso richiederebbe troppo tempo. Dobbiamo in qualche modo dare la priorità ai quadrati che hanno maggiori probabilità di ottenere un quadrato valido di lunghezza laterale N dopo aver applicato il processo dettagliato nel precedente punto elenco.

    Dato che ogni riga e colonna deve avere una somma di S , gli elementi hanno una media di S / N . Quindi, scegliere più elementi vicini a quella media dovrebbe aumentare le nostre possibilità.

    Per ogni I in [A ... B] , selezioniamo pseudo-casualmente un float tra 0 e (I - S / N) 2 + 1 e ordiniamo gli elementi di [A ... B] per i float selezionati. Manteniamo i primi numeri N 2 e li mettiamo in ordine di lettura in un quadrato.

    Supponendo una distribuzione perfettamente uniforme di tutti i numeri reali tra 0 e (I - S / N) 2 + 1 in ogni passaggio, tutti i quadrati hanno una probabilità diversa da zero di essere scelti, il che significa che il processo finirà alla fine.

Codice

q~          e# Read all input from STDIN and evaluate it.
:M;         e# Save "S" in M and discard it from the stack.
),>:R;      e# Transform "A B" into [A ... B], save in R and discard.
(:L         e# Save "N - 1" in L and keep it on the stack.
{           e# If L is non-zero:
  {         e#   Do:
    R{      e#     For each I in R:
      ML)d/ e#       Compute M/Double(L+1).
      -Y#   e#       Subtract the result from I and square the difference.
      )mr   e#       Add 1 and pick a non-negative Double below the result.
    }$      e#     Sort the values of I according to the picks.
    L/      e#     Split the shuffled R into chunks of length L.
    L<      e#     Keep only the first L chunks.
    2{      e#     Do twice:
      {     e#       For each row of the  L x L array.
        M1$ e#       Push M and a copy of the row.
        :+- e#       Add the integers of the row and subtract their sum from M.
        +   e#       Append the difference to the row.
      }%    e#
      z     e#       Transpose rows and columns.
    }*      e#
    :U:+    e#     Save the result in U and concatenate its rows.
    __O|    e#     Push two copies. Deduplicate the second copy.
    =R*     e#     Push R if all elements are unique, an empty array otherwise.
    -       e#     Remove the result's elements from U's elements.
  }g        e#   If the resulting array is non-empty, repeat the loop.
  U{        e#   For each row in U:
    :s      e#     Convert its integers into strings.
    _:,     e#     Copy and replace each string with its length.
    :e>     e#     Compute the maximum length.
    f{      e#     For each integer, push the maximum length; then
      Se[   e#       Left-pad the integer with spaces to that length.
    }       e#
  }%        e#
  z         e#   Transpose rows with columns.
  Sf*N*     e#   Join columns by spaces, rows by linefeeds.
}M?         e# Else, push M.
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.