Tâche
Étant donné l'entrée N, générez et sortez une grille NxN où chaque ligne, colonne et les deux diagonales contiennent les nombres 1 à N
(ou 0 à N
-1 si c'est plus facile).
Contribution
L'entrée est un entier positif N
. Il représente le nombre de colonnes et de lignes dans la grille. Pour ce problème, vous pouvez supposer N
que la taille sera raisonnable 4 ≤ N ≤ 8
ou ( 1 ≤ N ≤ 8
si vous optez pour le bonus ci-dessous).
Sortie
La sortie sera la grille N
× N
. Dans la grille, chaque ligne ne contient que les chiffres 1 à N
, chaque colonne ne contient que les chiffres 1 à N
et les deux diagonales de longueur N
(celle de (0,0)
à (N-1,N-1)
et celle de (0,N-1)
à (N-1, 0)
) ne contiennent que les chiffres de 1 à N
. Vous pouvez utiliser les chiffres de 0 à N−1
. Pour chacune N
, il existe de nombreuses solutions possibles, il vous suffit d'imprimer la première que vous trouvez. Vous n'avez pas besoin d'imprimer des espaces entre les chiffres.
Contraintes
Votre code devrait pouvoir produire des résultats de manière répétée pour N >= 7
. Autrement dit, si vous êtes en mesure d'exécuter et d'obtenir une solution à N = 7
partir de votre code à chaque fois, vous êtes bon. En termes de limite absolue, votre code devrait être en mesure de résoudre N = 7
en moins de 10 minutes chaque fois que vous l'exécutez (c'est-à-dire, si vous dépendez de nombres aléatoires, dans le pire des cas, votre code devrait toujours se terminer en moins de 10 minutes N = 7
) .
Exemples
Contribution:
4
Une sortie possible:
1 2 3 4 3 4 1 2 4 3 2 1 2 1 4 3
Contribution:
5
Une sortie possible:
1 2 3 4 5 5 3 1 2 4 2 5 4 3 1 4 1 2 5 3 3 4 5 1 2
Contribution:
8
Une sortie possible:
1 2 3 4 5 6 7 8 2 3 1 5 4 8 6 7 4 1 2 3 7 5 8 6 6 4 7 8 1 2 3 5 7 5 8 2 6 3 4 1 5 8 4 6 2 7 1 3 8 7 6 1 3 4 5 2 3 6 5 7 8 1 2 4
Notation
Il s'agit de code-golf , donc le code le plus court en octets l'emporte, à une exception près. Pour les entrées, N = 2, 3
il n'y a pas de solutions valides. Si votre code peut gérer cela (exécuter jusqu'à la fin sans sortir quoi que ce soit pour ces cas, ou sortir une chaîne vide), et gère toujours N = 1
(les sorties 1
pour cela), prenez 20% sur votre nombre d'octets.
la source
N
. Ce code JavaScript fonctionneN = 1, 5 or 7
cependant s'il aide quelqu'un:for(o="",y=N;y--;o+="\n")for(x=N;x--;)o+=(((N-y)*2+x)%N)+1
N = 1
cas: les réponses qui visent le bonus devraient retourner1
, pas la chaîne vide.Réponses:
Python 3,
275260 Octets * 0,8 =220208 OctetsApproche récursive / retour en arrière.
R
est la fonction récursive,l
est le coLumn,w
est le roW,K
est l'entrée suivante.J'ai choisi de le mettre dans un tableau 1d et de l'imprimer à la fin pour simplifier les indices.
Version non golfée:
la source
Funciton , non compétitif
MISE À JOUR! Amélioration massive des performances! n = 7 se termine maintenant en moins de 10 minutes! Voir explication en bas!
C'était très amusant à écrire. Il s'agit d'un solveur de force brute pour ce problème écrit en Funciton. Quelques factoids:
3 2 1 0
plutôt que là où la ligne de haut lit0 1 2 3
.0
(la seule solution) pour n = 1.Sans plus tarder:
Explication de la première version
La première version a pris environ une heure pour résoudre n = 7. Ce qui suit explique principalement comment cette version lente fonctionnait. En bas, j'expliquerai quel changement j'ai fait pour le réduire à moins de 10 minutes.
Une excursion en morceaux
Ce programme a besoin de bits. Il a besoin de beaucoup de bits et il en a besoin aux bons endroits. Les programmeurs Funciton expérimentés savent déjà que si vous avez besoin de n bits, vous pouvez utiliser la formule
qui dans Funciton peut être exprimé comme
Lors de l'optimisation des performances, il m'est venu à l'esprit que je peux calculer la même valeur beaucoup plus rapidement en utilisant cette formule:
J'espère que vous me pardonnerez que je n'ai pas mis à jour tous les graphiques d'équation dans ce post en conséquence.
Maintenant, disons que vous ne voulez pas d'un bloc de bits contigu; en fait, vous voulez n bits à intervalles réguliers chaque k -ième bit, comme ceci:
La formule pour cela est assez simple une fois que vous le savez:
Dans le code, la fonction
Ӝ
prend les valeurs n et k et calcule cette formule.Garder une trace des numéros utilisés
Il y a n ² nombres dans la grille finale, et chaque nombre peut être n'importe laquelle des n valeurs possibles. Afin de garder une trace des nombres autorisés dans chaque cellule, nous conservons un nombre composé de n ³ bits, dans lequel un bit est défini pour indiquer qu'une valeur particulière est prise. Initialement, ce nombre est 0, évidemment.
L'algorithme commence dans le coin inférieur droit. Après avoir «deviné» que le premier nombre est un 0, nous devons garder une trace du fait que le 0 n'est plus autorisé dans aucune cellule de la même ligne, colonne et diagonale:
À cette fin, nous calculons les quatre valeurs suivantes:
Ligne actuelle: nous avons besoin de n bits tous les n -ièmes bits (un par cellule), puis de la déplacer vers la ligne actuelle r , en se souvenant que chaque ligne contient n ² bits:
Colonne actuelle: nous avons besoin de n bits tous les n ²-ème bits (un par ligne), puis de la déplacer vers la colonne actuelle c , en se souvenant que chaque colonne contient n bits:
Diagonale avant: Nous avons besoin de n bits tous les ... (avez-vous fait attention? Vite, comprenez-le!) ... n ( n +1) -ème bit (bien joué!), Mais seulement si nous sommes réellement sur la diagonale avant:
Diagonale arrière: Deux choses ici. Tout d'abord, comment savoir si nous sommes sur la diagonale arrière? Mathématiquement, la condition est c = ( n - 1) - r , qui est la même que c = n + (- r - 1). Hé, ça te rappelle quelque chose? C'est vrai, c'est le complément à deux, nous pouvons donc utiliser la négation au niveau du bit (très efficace dans Funciton) au lieu de la décrémentation. Deuxièmement, la formule ci-dessus suppose que nous voulons que le bit le moins significatif soit défini, mais dans la diagonale arrière, nous ne le faisons pas, nous devons donc le décaler vers le haut de ... savez-vous? ... C'est vrai, n ( n - 1).
C'est aussi le seul où nous divisons potentiellement par 0 si n = 1. Cependant, Funciton s'en fiche. 0 ÷ 0 est juste 0, tu ne sais pas?
Dans le code, la fonction
Җ
(celle du bas) prend n et un indice (à partir duquel elle calcule r et c par division et reste), calcule ces quatre valeurs et lesor
s ensemble.L'algorithme de force brute
L'algorithme de force brute est implémenté par
Ӂ
(la fonction en haut). Cela prend n (la taille de la grille), l' index (où dans la grille nous plaçons actuellement un nombre), et pris (le nombre avec n ³ bits nous indiquant quels nombres nous pouvons encore placer dans chaque cellule).Cette fonction renvoie une séquence de chaînes. Chaque chaîne est une solution complète à la grille. C'est un solveur complet; il retournerait toutes les solutions si vous le permettez, mais il les renvoie comme une séquence évaluée paresseusement.
Si l' index a atteint 0, nous avons réussi à remplir toute la grille, nous retournons donc une séquence contenant la chaîne vide (une solution unique qui ne couvre aucune des cellules). La chaîne vide est
0
, et nous utilisons la fonction de bibliothèque⌑
pour la transformer en une séquence à un seul élément.La vérification décrite sous l' amélioration des performances ci-dessous se produit ici.
Si l' index n'a pas encore atteint 0, nous le décrémentons de 1 pour obtenir l'index auquel nous devons maintenant placer un nombre (appelons cela ix ).
Nous utilisons
♫
pour générer une séquence paresseuse contenant les valeurs de 0 à n - 1.Ensuite, nous utilisons
ɓ
(liaison monadique) avec un lambda qui effectue les opérations suivantes dans l'ordre:0
(séquence vide).Җ
pour calculer les bits correspondant à la ligne, la colonne et la ou les diagonales actuelles. Déplacez-le par i puisor
sur prise .Ӂ
pour récupérer toutes les solutions pour les cellules restantes, en lui passant la nouvelle prise et la ix décrémentée . Cela renvoie une séquence de chaînes incomplètes; chaque chaîne a ix caractères (la grille remplie jusqu'à l'index ix ).ɱ
(map) pour parcourir les solutions ainsi trouvées et utilisez‼
pour concaténer i à la fin de chacune. Ajoutez une nouvelle ligne si l' index est un multiple de n , sinon un espace.Générer le résultat
Le programme principal appelle
Ӂ
(le forceur brut) avec n , index = n ² (rappelez-vous que nous remplissons la grille à l'envers) et pris = 0 (initialement rien n'est pris). Si le résultat est une séquence vide (aucune solution trouvée), sortez la chaîne vide. Sinon, sortez la première chaîne de la séquence. Notez que cela signifie qu'il n'évaluera que le premier élément de la séquence, c'est pourquoi le solveur ne continue pas tant qu'il n'a pas trouvé toutes les solutions.Amélioration des performances
(Pour ceux qui ont déjà lu l'ancienne version de l'explication: le programme ne génère plus une séquence de séquences qui doit être séparément transformée en chaîne pour la sortie; il génère simplement une séquence de chaînes directement. J'ai édité l'explication en conséquence Mais ce n'était pas la principale amélioration. Ça y est.)
Sur ma machine, l'exe compilé de la première version a pris à peu près exactement 1 heure pour résoudre n = 7. Ce n'était pas dans le délai imparti de 10 minutes, donc je ne me suis pas reposé. (Eh bien, en fait, je ne me suis pas reposé parce que j'avais cette idée sur la façon d'accélérer massivement.)
L'algorithme tel que décrit ci-dessus arrête sa recherche et revient en arrière chaque fois qu'il rencontre une cellule dans laquelle tous les bits du nombre pris sont définis, indiquant que rien ne peut être mis dans cette cellule.
Cependant, l'algorithme continuera à remplir vainement la grille jusqu'à la cellule dans laquelle tous ces bits sont définis. Il serait beaucoup plus rapide si elle pouvait arrêter dès que toute cellule en encore à être rempli a déjà tous les bits mis, ce qui indique déjà que nous ne pouvons jamais résoudre le reste de la grille , peu importe ce que les numéros que nous mettons dans il. Mais comment vérifier efficacement si une cellule a ses n bits définis sans les parcourir tous?
L'astuce commence par l'ajout d'un seul bit par cellule au nombre pris . Au lieu de ce qui a été montré ci-dessus, cela ressemble maintenant à ceci:
Au lieu de n ³, il y a maintenant n ² ( n + 1) bits dans ce nombre. La fonction qui remplit la ligne / colonne / diagonale actuelle a été modifiée en conséquence (en fait, complètement réécrite pour être honnête). Cependant, cette fonction ne remplira que n bits par cellule, donc le bit supplémentaire que nous venons d'ajouter sera toujours
0
.Maintenant, disons que nous sommes à mi-chemin du calcul, nous venons de placer un
1
dans la cellule du milieu, et le nombre pris ressemble à ceci:Comme vous pouvez le voir, la cellule en haut à gauche (index 0) et la cellule au milieu à gauche (index 10) sont désormais impossibles. Comment pouvons-nous le déterminer le plus efficacement possible?
Considérez un nombre dans lequel le 0e bit de chaque cellule est défini, mais uniquement jusqu'à l'index actuel. Un tel nombre est facile à calculer en utilisant la formule familière:
Qu'obtiendrions-nous si nous additionnions ces deux chiffres ensemble?
Le résultat est:
Comme vous pouvez le voir, l'addition déborde dans le bit supplémentaire que nous avons ajouté, mais seulement si tous les bits de cette cellule sont définis! Par conséquent, tout ce qui reste à faire est de masquer ces bits (même formule que ci-dessus, mais << n ) et de vérifier si le résultat est 0:
S'il n'est pas nul, la grille est impossible et nous pouvons nous arrêter.
la source
Haskell, 790 * 0,80 = 632 octets
J'ai remarqué que ce problème est très similaire à sudoku. Je me souviens d'un ancien solveur sudoku que j'ai écrit dans Haskell basé sur cet autre en Python. Ceci est mon premier message ou tentative de golf à code.
Cela remplit le bonus car il revient
Nothing
pourn=2,3
etJust <result>
pourn>=4
, où<result>
est un tableau 2D de valeurs intégrales.Voir ici pour l'interprète en ligne. Ce code est en fait plus long que celui de la publication car l'interprète en ligne a des exigences plus strictes quant à ce qui constitue un programme complet (les règles disent qu'une soumission peut être une fonction). Cette soumission prend l'entrée comme argument de fonction.
la source
c=[1..r]
, vous pouvez donc l'utiliser danso
etw
. b)minimumBy(\(a,_)(b,_)->compare a b)[...]
esthead$sortOn fst[...]
. c)v
env=g0!s
est utilisé qu'une seule fois, il ne définit pas du tout:l=length$g0!s
. d) vous avez encore quelques noms de paramètres à deux lettres. e) remplacerTrue
par1<2
etFalse
par2<1
. f)and[case g0!s of{[_]->True;_->False}|s<-u]
estall((==1).length.(g0!))u
.(&)m k=
peuvent être définis infix:m&k=
. h) l'not(d
elem l'(g0!p))
estnotElem d$g0!p
. i)concat(...)
estid=<<(...)
. j) utiliser un opérateur infixe pourh
, par exempleas%bs=
.``like`this``
!Pyth, 41 octets
Force brute ftw!
Étant donné que cela continue essentiellement d'essayer des shuffles aléatoires jusqu'à ce qu'il fonctionne (enfin, il continue d'essayer
n * [shuffle(range(n))]
), cela prend vraiment très très longtemps. Voici quelques essais pour vous donner une idée du temps que cela prend:Ce n'est que du 4x4 et il fonctionne en un peu moins d'une demi-seconde. En fait, je triche parce que c'est le meilleur de quelques essais - la plupart prennent plus d'une seconde ou deux.
Je n'ai pas encore de chronométrage sur 5x5 (il s'est terminé une fois, mais c'était dans un REPL et je ne le chronométrais pas).
Notez que la règle du délai n'a été modifiée dans la question qu'après la publication de cette réponse.
la source
SWI-Prolog, 326 * 0,80 = 260,8 octets
Edit: économisé 16 octets grâce à @Mat
Usage
Appelez
a(5).
votre interprète pourN=5
. Cela revientfalse
pourN=2
ouN=3
.Puisqu'il utilise la bibliothèque CLPFD, ce n'est pas de la pure bruteforce. Ce programme peut trouver une solution pendant
N=20
environ 15 secondes sur mon ordinateur.Non golfé + explications:
Cela fonctionne essentiellement comme un solveur Sudoku, sauf que les contraintes de blocs sont remplacées par les contraintes de diagonales.
la source
maplist(maplist(all_distinct), [R,C,D,E])
[R,C,[D,E]]
cependant, carE
etD
sont de simples listes.N=20
CJam, 87 octets - 20% de bonus = 69,6 octets
Codez en dur les réponses. Contient certains non imprimables. Fonctionne pour à
N = 1
traversN = 8
.Fondamentalement, chaque ligne de cette mystérieuse chaîne contient des indices dans la liste des permutations de
range(N)
, codés en caractères Unicode.=:i0+\,m!f=
indexe dans la liste des permutations, en ajoutant d'abord un 0 à la fin de la liste des index, représentant la ligne du bas0 1 2 ... N-1
. PourN < 4
, le tableau 2D résultant est un non-sens.`1LL]
crée une liste[N, pretty output, 1, "", ""]
. Ensuite,(4e<=
sort le premier élément de cette listeN
et récupère lemin(N, 4) % 4
th élément du reste. PourN >= 4
, c'est la sortie, et sinon ce sont les sorties de cas spécial pour les trois premiers cas.Essayez-le ici .
la source
C ++,
672 * 0,80645 * 0,80 = 516 octetsEssayez-le en ligne ici
Étant donné que quelques réponses ont déjà été publiées, j'ai pensé publier la version golf du code que j'ai utilisé pour générer la sortie des exemples. C'est la première fois que je réponds à un code-golf , donc tous les commentaires sont les bienvenus. :)
Non golfé + explications:
Essentiellement, le code force brutalement une solution. Il commence dans la première rangée, avec 0. Il commence dans la première tache, si ces taches passent tous les contrôles, il passe au numéro suivant. S'il remplit la ligne, il passe à la ligne suivante. Si c'est fait toutes les lignes, cela signifie qu'une solution a été trouvée. Si le spot ne passe pas tous les contrôles, il passe au spot suivant. Si c'est fait la ligne, alors il revient en arrière, car un nombre dans l'une des lignes précédentes empêche une solution d'être possible.
la source
if (x[r][p]) return f(c+1,r);
. Je travaille à le raccourcir.Clojure,
(215 + 258) * 0,8 = 378,4(174 + 255) * 0,8 = 343,2Divisé en deux parties: le comptage des erreurs
S
et une fonction anonyme qui fait l'optimisation réelle via la recherche Tabu .Mise à jour: plus courte
S
(compte des valeurs distinctes au sein des groupes), état de départ moins optimal (pas de mélange).Les benchmarks monocœurs (en millisecondes) pour 4, 5, 6 et 7 s'exécutent 3 fois:
Original:
Je souhaite que ce
S
soit plus court, mais comme il ne compte que les occurrences de plus d'une partition, le critère d'arrêt est simple(= s 0)
.De nombreux cycles CPU sont gaspillés pour des échanges non utiles, par exemple, cela n'améliore pas le score si vous échangez
2
avec2
, et vous n'avez pas besoin d'échanger des nombres entre les lignes car ils ont tous des valeurs distinctes pour commencer.Benchmarks avec Intel 6700K (en millisecondes):
Multithread avec
pmap
:la source