Problème “Remplissez la grille”

36

Un défi avec des règles simples mais des algorithmes non triviaux. :-)

Tâche

Prendre une entrée sous forme d’entiers séparés par des espaces:

N A B S

Où N est la longueur de côté d'une matrice carrée 2D remplie de nombres uniques (entiers) compris entre A et B inclus. Pour chaque ligne et colonne de cette matrice, la somme est toujours la même: S. (En d'autres termes, la matrice est un carré semi-magique).

Remarque:

Tous les chiffres sont positifs. L'exception est A, qui peut être 0.

Exemples

Pour

3 1 10000 2015

une solution valable serait

entrez la description de l'image ici

Pour

8 1 300 500

une solution valable serait

entrez la description de l'image ici

Sortie

Votre sortie devrait être une table ASCII. Exemple pour le premier exemple ci-dessus:

 384  159 1472
1174  499  342
 457 1357  201

Entiers alignés à droite remplis d'espaces. La largeur de chaque colonne est la largeur du plus grand entier de cette colonne.

Notation

C'est du , donc le code le plus court en octets gagne. Les failles standard s’appliquent (en particulier sur les solutions intégrées pour résoudre ce problème). Vous n'avez pas à vous soucier d'entrées fausses ou autrement impossibles (y compris des nombres négatifs). Veuillez fournir un exemple de sortie dans votre réponse (obligatoire) pour le deuxième exemple ci-dessus.

mınxomaτ
la source
1
Sommes-nous autorisés à générer des nombres aléatoires entre A et B jusqu'à ce qu'ils totalisent correctement et soient uniques?
lirtosiast
Juste pour vérifier, A, Bet Npeut être négatif?
xnor
2
Minxomat, je ne dis pas que c'est la meilleure solution que je puisse trouver, je dis que c'est peut-être la plus courte possible.
lirtosiast
3
@LuisMendo Vous devez générer un exemple de sortie en fonction de la tâche. Si vous pouviez gérer cela de votre vivant avec une approche de force brute, je serais impressionné. :-) Je pourrais l'exclure, mais ce serait trop flou, car la solution la plus populaire (GA) implique toujours le caractère aléatoire. Aussi, je ne veux pas changer les règles quand quelqu'un pourrait déjà travailler sur une solution.
mınxomaτ
1
@minxomat Tous vos trois arguments sont de très bons arguments :-)
Luis Mendo

Réponses:

19

CJam, 119 91 octets

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?

Il s’agit d’une approche parfaitement déterminante et non déterministe.

Sur mon bureau, le deuxième cas de test se termine généralement en moins de 10 minutes.

Le premier cas se termine instantanément. Essayez-le en ligne dans l' interprète CJam .

Échantillon échantillon

$ 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

Idée

Sans limite de temps, nous pourrions simplement générer des carrés au hasard jusqu'à trouver un carré valide. Cette approche s'appuie sur cette idée en ajoutant deux optimisations:

  • Au lieu de générer de manière pseudo-aléatoire un carré de côtés N , nous générons des carrés de côtés N-1 , ajoutons une colonne pour former un rectangle N × (N-1) dont les lignes ont la somme S , puis une ligne pour former un carré de longueur latérale N dont les colonnes ont somme S .

    Etant donné que la somme des éléments des colonnes sera NS et la somme des éléments du premier N-1 lignes est (N-1) S , la dernière ligne aura également somme S .

    Toutefois, ce processus peut générer une matrice non valide, car rien ne garantit que tous les éléments de la dernière ligne et de la dernière colonne seront uniques ou tomberont dans la plage [A ... B] .

  • Choisir un carré d'entiers uniques dans [A ... B] et la longueur du côté N-1 uniformément au hasard prendrait beaucoup trop de temps. Nous devons en quelque sorte donner la priorité aux carrés qui ont plus de chances d’aboutir à un carré valide de longueur de côté N après avoir appliqué le processus détaillé dans le point précédent.

    Étant donné que chaque ligne et colonne doit avoir une somme de S , ses éléments ont une moyenne de S / N . Ainsi, choisir plus d'éléments proches de cette moyenne devrait augmenter nos chances.

    Pour chaque I dans [A ... B] , nous sélectionnons de manière pseudo-aléatoire un flottant compris entre 0 et (I - S / N) 2 + 1 et nous trions les éléments de [A ... B] en fonction des flottants sélectionnés. Nous conservons les premiers N 2 chiffres et les plaçons en ordre de lecture dans un carré.

    En supposant une distribution parfaitement uniforme de tous les nombres réels entre 0 et (I - S / N) 2 + 1 à chaque étape, tous les carrés ont une probabilité non nulle d'être sélectionnés, ce qui signifie que le processus s'achèvera éventuellement.

Code

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.
Dennis
la source