- Dans Dyalog APL,
sudoku renvoie toutes les matrices solution possibles à partir d’une matrice de puzzle où les cases vides valent 0, et le même problème est implémenté de plusieurs façons dans le style APL/K
- La cible de base est un Sudoku 9×9, dans lequel chaque bloc 3×3, ligne et colonne doit contenir les chiffres de 1 à 9 sans répétition
- L’entrée
prob contient les valeurs 1-9 dans les cases remplies et 0 dans les cases vides, et l’argument gauche optionnel shape permet aussi de définir des blocs non carrés comme 2×3 ou 3×4
- L’algorithme de résolution de Veli-Matti Jantunen vectorise la matrice, construit des index de lignes, colonnes et blocs, puis réduit progressivement les candidats en développant d’abord le groupe le plus contraint
- Les exemples
s33 et s22 ont chacun 3 solutions, et 3 4 sudoku s34 en a 2 ; le one-liner en K 5 d’Arthur Whitney et plusieurs réimplémentations APL sont également présentés
Entrée Sudoku et résultat de la fonction sudoku
- Un puzzle Sudoku est une grille où des blocs 3×3 sont disposés en 3×3 ; chaque case est vide ou contient un chiffre de 1 à 9
- Une solution doit satisfaire les trois contraintes d’absence de doublons
- chaque bloc 3×3 contient les chiffres de 1 à 9 sans répétition
- chaque ligne de 9 cases contient les chiffres de 1 à 9 sans répétition
- chaque colonne de 9 cases contient les chiffres de 1 à 9 sans répétition
- La matrice
prob utilise les chiffres 1-9 pour les cases remplies et 0 pour les cases vides
- L’argument gauche optionnel
shape spécifie la forme des blocs pour les puzzles qui ne suivent pas la forme carrée par défaut
- pour une matrice 6×6 dont les sous-zones sont en 2×3, on appelle la fonction sous la forme
2 3 sudoku mat
- Le résultat est un vecteur contenant toutes les matrices solution
- s’il n’existe aucune solution, la fonction renvoie
⍬
- les situations d’erreur peuvent être représentées par
'' ; la documentation indique « cela ne devrait pas arriver, mais peut se produire si le nombre de résultats devient énorme »
Déroulement de la solution de Veli-Matti Jantunen
- L’algorithme traite la matrice Sudoku comme un vecteur et représente séparément les lignes, colonnes et zones Sudoku par des vecteurs d’index
- Après les vérifications de base, il examine les alternatives une à une dans la liste des candidats
- À chaque étape, il filtre les éléments possibles de toutes les cases
- si au moins une case n’a aucune valeur possible, le candidat de solution est éliminé
- si une case a deux chiffres candidats ou plus, l’algorithme choisit une case dans le groupe le plus contraint et ajoute les combinaisons candidates de cette case à la liste
- si toutes les cases n’ont plus qu’un seul chiffre possible, la grille est traitée comme une solution et l’algorithme passe au candidat suivant
- La même section inclut aussi une fonction
Shuffle qui mélange une grille Sudoku existante pour en produire une autre
Le one-liner d’Arthur Whitney et les implémentations alternatives
- L’implémentation alternative de
sudoku par David Crossley prend en entrée une configuration N×N et vise les cas où la taille de bloc N*÷2 est entière
- l’entrée doit être une disposition valide dans laquelle certaines cases contiennent des chiffres de 1 à
N et les autres 0
- dans le résultat, chaque ligne, colonne et bloc doit contenir tous les chiffres de 1 à
N
- l’implémentation s’appuie en interne sur des fonctions auxiliaires comme
valid, search, rules, sole, singles, uniques, matches, NinN et setup
- La solution en K 5 d’Arthur Whitney est présentée sous forme de code sur une seule ligne
x(,/{@[x;y;]'(!10)^x*|/p[;y]=p,:,3/:-3!p:!9 9}')/&~* x
- Phil Last propose une implémentation
sudoku qui transpose le code de Whitney en D-function
- La réécriture de Morten Kromberg définit explicitement certains composants de K pour rester plus proche de l’original
- comme la version K, elle prend en entrée et renvoie un vecteur de 81 éléments plutôt qu’une matrice
- L’implémentation
Sudoku de Roger Hui est plus généralisée et traite aussi les puzzles à blocs non carrés
svec construit le vecteur solution, et pvex ainsi que pvec développent les placements possibles
avl construit la liste des chiffres possibles, et emt trouve les index de ligne et de colonne des cases vides
rcb, box, cmap et CMAP construisent les relations de conflit entre lignes, colonnes et blocs
Puzzles d’exemple et nombre de solutions
s33 est un exemple 9×9, et le résultat de sudoku s33 comporte 3 solutions
- La fonction
sbox découpe les blocs internes afin d’afficher la grille Sudoku plus lisiblement
- les 0 sont affichés sous forme de points (
·)
- la sortie prend la forme d’une matrice de caractères avec tracé des frontières entre blocs
s22 est un exemple 4×4, et le résultat de sbox¨ sudoku s22 comporte 3 solutions
s34 est un exemple utilisant des blocs 3×4
3 4 sbox s34 affiche le puzzle avec séparation visuelle des blocs
- le résultat de
3 4 sudoku s34 comporte 2 solutions
Liens de référence et éléments associés
1 commentaires
Avis sur Hacker News
Cette ligne est écrite en K. K est un langage créé par Arthur Whitney à partir d’APL et de Scheme
x(,/{@[x;y;]'(!10)^x*|/p[;y]=p,:,3/:-3!p:!9 9}')/&~*xJ’évalue parfois la complexité d’un code en comparant son nombre de lignes au résultat de la commande suivante
tar -cf - . | gzip | base64 | wc -lAutrement dit, je regarde « à quel point ça se compresse bien ». Quand je vois de l’APL, ça me rappelle les fois où l’on envoie par erreur la sortie de gzip dans le terminal
p←{(↑⍵)∘{(⍺∨.=⍵)/⍳n×n∘}¨,⍵},(n*÷2){⍵,⍺⊥⌊⍵÷⍺}'⍳n n←⍴⍵C’est impressionnant qu’il y ait même des gens capables de suivre ce genre de code en se demandant « peut-on y trouver un bug ? ». On dirait des données binaires compressées dont tout le monde posséderait déjà le même dictionnaire
∘sans opérande droit, etn n←⍴⍵semble indiquer quenest affecté deux fois et que l’on s’attend à ce que⍵soit bidimensionnel, mais selon l’intention,_ n←⍴⍵oun←⊃⌽⍴⍵serait plus naturelDe plus,
⊥provoque une erreur si⍴⍵n’est ni un entier unique ni un vecteur vide, si bien qu’au final ce n’est pas vraiment différent den←⍴⍵, ce qui rend la chose encore plus déroutante. Plusieurs,redondants et↑⍵peuvent aussi être supprimés, et toute l’expression revient en fait presque àp←(n+1)⍴⊂⍳n×n←⍴⍵, une structure qui produitn+1vecteurs1..n²Malgré son apparence étrange, une fois qu’on apprend les symboles et les opérations de base, APL est étonnamment linéaire. Il faut toutefois du temps pour devenir à l’aise, et quand on y arrive, cela donne l’impression d’avoir un super-pouvoir
Il est vrai que les défenseurs du langage mettent en avant sa vitesse, la facilité du traitement de tableaux et sa syntaxe expressive
https://en.m.wikipedia.org/wiki/K_(programming_language)
Le nombre de lignes de code est un mauvais indicateur, car chaque langage a sa propre manière d’utiliser les lignes
Une meilleure mesure pourrait consister à compter le nombre de nœuds de l’arbre syntaxique correspondant à des symboles non terminaux significatifs, comme les « constantes » ou les « appels de fonction ». Mieux encore, on pourrait aussi tenir compte de la profondeur de cet arbre et de son facteur de branchement
Une solution en une ligne n’occupe presque pas d’espace à l’écran, ce qui est un gros avantage quand on traite des problèmes complexes. Déplacer les yeux dans l’écran demande beaucoup moins d’effort que passer d’un fichier à l’autre en faisant défiler, et la charge cognitive compte
Même sans connaître K, voir les constantes alignées donne l’impression d’utiliser une représentation directe des données du problème. Si la culture de K encourage ce genre de code et oriente la pensée vers la directeté et la simplicité, j’aimerais bien introduire cette sauce spéciale dans une équipe
https://cliffle.com/esoterica/hq9plus/
https://en.wikipedia.org/wiki/Algorithmic_information_theory
Je me suis souvent demandé si l’utilisation de langages comme APL/K permet réellement aux programmeurs de penser les problèmes plus efficacement
avg a+bDans un langage non centré sur les tableaux, il faudrait probablement des vérifications de bornes, une grosse boucle
for, des variables temporaires pour la somme et le nombre d’éléments, etc. Ce qui prendrait environ 6 lignes dans un langage comme C se fait en 6 caractères en QCela dit, chaque langage possède des fonctionnalités qui aident à raisonner sur certains problèmes. Les langages fonctionnels avec types algébriques et filtrage par motif, comme OCaml ou F#, sont meilleurs qu’un gros
switchou une chaîne deif-else-if, et les langages avec du sucre syntaxique commeasync/awaitsont avantagés pour gérer la concurrenceQuand je travaillais comme quant, j’ai beaucoup utilisé kdb+/q pendant plus de cinq ans pour des stratégies de moyenne fréquence, mais lorsque je suis passé au trading haute fréquence, avec des calculs sur carnet d’ordres difficiles ou peu efficaces à vectoriser, continuer à utiliser un langage centré sur les tableaux rendait au contraire le raisonnement plus complexe
https://youtu.be/PlM9BXfu7UY?si=ORtwI1qmfmzhJGZX&t=3598
Le passage concerné s’inscrivait dans le contexte d’un compilateur, mais l’ensemble de la présentation traite Dyalog et APL comme un système de notation mathématique. L’idée centrale est qu’il peut être plus facile d’optimiser des expressions mathématiques que du code ordinaire
L’un des points les plus importants ici est que le générateur de problèmes en haut est très clair. C’est ce qui distingue les langages notationnels à la Iverson, y compris J et K, des autres langages.
Il n’a pas l’élégance ni la puissance de la solution en une ligne, mais il est très propre et compréhensible même sans commentaires stricts. Cela dit, je ne pense pas que
lampsoit un bon symbole de commentaire.La solution en une ligne est étonnante, et la programmation tacite est à vous retourner le cerveau. L’idée d’utiliser la concision particulière des langages à glyphes pour expliquer et pratiquer la programmation fonctionnelle, puis de l’appliquer à des tableaux entiers, relève du génie.
https://www.jsoftware.com/papers/fork.htm
Bien sûr, si l’on retire cette possibilité, on peut forcer l’écriture d’un code plus verbeux, mais cela réduit fortement leur intérêt comme outils interactifs. Les langages à la Iverson permettent d’écrire du code très court, ce qui les rend utiles pour le travail interactif. Dans ce contexte, le code n’est même pas enregistré, c’est donc vraiment du code write-only.
Quand on écrit du code destiné à aller dans un fichier, on choisit le style que l’on veut, et dans ce cas je recommande d’écrire de façon moins compacte. Même ainsi, les langages à la Iverson produisent, dans un style verbeux, un code bien plus court que la plupart des langages.
La plupart des gens sont rebutés par les symboles, mais ce n’était pas mon problème.
J’aime APL et les langages de tableaux, et ce que j’ai appris m’a beaucoup aidé dans d’autres langages. Mais ils ne sont pas devenus mes outils du quotidien ; pas à cause des symboles, plutôt parce qu’après 3 ou 4 ans d’utilisation intermittente, je me suis heurté à un mur que je n’ai pas réussi à franchir.
Dans les autres langages, il existe généralement une approche générale qui permet de résoudre un problème, même grossièrement, puis, quand on découvre le « truc » propre à ce problème, on peut le rendre plus élégant et plus efficace. Avec APL, j’avais l’impression qu’il n’y avait pas ce genre de détour provisoire : soit on connaît le truc, soit on ne le connaît pas.
Je ne sais pas vraiment si c’est effectivement le cas, si l’on développe une intuition de résolution de problèmes en apprenant suffisamment de trucs, si cela reste jusqu’au bout une affaire de trucs, ou si je suis simplement passé à côté du document décrivant les stratégies essentielles.
⍸⍣¯1», alors qu’il est fort possible que personne ne vous ait jamais dit que⍸avait une opération inverse ni comment l’utiliser.Même après des années à utiliser ces langages, les murs de code que produisent certains programmeurs de tableaux restent un peu intimidants. Je comprends pourquoi ils écrivent ainsi, mais personnellement je préfère qu’il y ait un peu d’espace dans le code.
Je suis en train de créer un langage de tableaux basé sur APL, et l’un de mes objectifs initiaux était de faire du style impératif un citoyen de première classe, sans punir les débutants qui utilisent des choses comme des instructions
if. Je vois ce style comme un entre-deux entre l’APL pur et les langages impératifs classiques.https://codeberg.org/loke/array/src/branch/master/array/standard-lib/output3.kap
Cela ne signifie pas non plus que ce soit une limite du langage lui-même. D’après mon expérience, franchir ce mur correspond justement au moment où le paradigme se met en place. Ce n’est qu’après avoir bricolé pendant environ 500 heures sur un prototype de parseur YAML pendant un an que les pièces ont commencé à s’assembler.
Le cœur du sujet me semble être une combinaison de principes de conception pilotée par les données, de l’usage concret des propriétés iversoniennes d’une bonne notation dans l’architecture logicielle, et de la familiarité avec les idiomes ainsi qu’avec la manière dont ils expriment les concepts du domaine.
https://dyalog.tv/Dyalog23/?v=J4cg6SV92C4
https://www.jsoftware.com/papers/tot.htm
Il existe une vidéo sur ce sujet.
https://www.youtube.com/watch?v=DmT80OseAGs
On peut essayer la solution directement sur https://tryapl.org/.
Il pourrait être intéressant de comparer cette ligne unique aux solutions de code golf dans plusieurs langages de programmation.
https://codegolf.stackexchange.com/questions/tagged/sudoku?tab=Votes
https://codegolf.stackexchange.com/a/5030