<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Python | Dr. Goulu</title><link>https://drgoulu.com/tags/python/</link><atom:link href="https://drgoulu.com/tags/python/index.xml" rel="self" type="application/rss+xml"/><description>Python</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>fr-FR</language><lastBuildDate>Sat, 23 Jun 2018 00:00:00 +0000</lastBuildDate><image><url>https://drgoulu.com/media/icon_hu_eee4a95885829ab2.png</url><title>Python</title><link>https://drgoulu.com/tags/python/</link></image><item><title>Les finesses de Python</title><link>https://drgoulu.com/2018/06/23/les-finesses-de-python/</link><pubDate>Sat, 23 Jun 2018 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2018/06/23/les-finesses-de-python/</guid><description>&lt;p&gt;J&amp;rsquo;ai donné une petite conférence au 
 sur certains aspects &amp;ldquo;avancés&amp;rdquo; du langage Python :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Générateurs, iterateurs et programmation fonctionnelle&lt;/li&gt;
&lt;li&gt;Introspection&lt;/li&gt;
&lt;li&gt;Décorateurs
&lt;ul&gt;
&lt;li&gt;exemples : Memoïsation et Timeout&lt;/li&gt;
&lt;li&gt;Prédéfinis et “multiméthodes”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J&amp;rsquo;avais utilisé un “notebook 
” comme support, et je viens de
, alors voilà:&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;l&amp;rsquo;erreur à la fin est normale, si vous avez bien suivi&amp;hellip;&lt;/p&gt;
&lt;p&gt;Le repo du document est ici : https://github.com/goulu/python_finesses&lt;/p&gt;</description></item><item><title>A303935 , ma première suite OEIS</title><link>https://drgoulu.com/2018/06/09/a303935-ma-premiere-sequence-oeis/</link><pubDate>Sat, 09 Jun 2018 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2018/06/09/a303935-ma-premiere-sequence-oeis/</guid><description>&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt=""
srcset="https://drgoulu.com/posts/2018/images/euler-level2-2_hu_a1389026d67b5dfc.webp 292w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://drgoulu.com/posts/2018/images/euler-level2-2_hu_a1389026d67b5dfc.webp"
width="292"
height="322"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
Depuis le temps que je joue avec l&amp;rsquo;
 (
), ça a fini par arriver : j&amp;rsquo;ai réussi à y ajouter une nouvelle suite,  
!&lt;/p&gt;
&lt;p&gt;Tout a commencé avec le
, qui traite de la somme des factorielles des chiffres (dfs) des nombres  (
). Seuls quatre d&amp;rsquo;entre eux appelés
sont égaux à la somme des factorielles de leurs chiffres : 1, 2, 145 et 40585  (
).&lt;/p&gt;
&lt;p&gt;Quelqu&amp;rsquo;un a prouvé en 2004 &lt;/p&gt;
\[1\]&lt;p&gt; que l&amp;rsquo;application répétée de la fonction dfs atteint immanquablement un des 4 factorions ou l&amp;rsquo;une des trois seules et uniques &amp;ldquo;boucles&amp;rdquo; suivantes:&lt;/p&gt;
&lt;p&gt;169 → 363601 → 1454 → 169 871 → 45361 → 871 872 → 45362 → 872&lt;/p&gt;
&lt;p&gt;Ne restait plus qu&amp;rsquo;à compter la longueur de l&amp;rsquo;
de dfs(n), et c&amp;rsquo;est précisément ce que liste 
, qui n&amp;rsquo;était pas encore répertoriée dans l&amp;rsquo;OEIS.&lt;/p&gt;
&lt;h2 id="publier-dans-loeis-cest-du-sérieux"&gt;Publier dans l&amp;rsquo;OEIS, c&amp;rsquo;est du sérieux.&lt;/h2&gt;
&lt;p&gt;Le processus de publication dans l&amp;rsquo;OEIS est très strict, sur le modèle de certaines publications scientifiques. &amp;ldquo;The standards are those of a mathematics reference work.&amp;rdquo; comme ils le mentionnent.&lt;/p&gt;
&lt;p&gt;Après s&amp;rsquo;
 il faut préparer tout le contenu comme pour un &amp;ldquo;papier&amp;rdquo;, en citant d&amp;rsquo;autres séries liées et des publications scientifiques.  Ensuite des &amp;ldquo;reviewers&amp;rdquo; ne se privent pas de demander des précisions et clarifications, voire de corriger la ponctuation&amp;hellip; C&amp;rsquo;est vraiment strict, et ça peut prendre un mois,
 qui a nécessité 40 itérations avant publication.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai failli abandonner car l&amp;rsquo;édition de 
  m&amp;rsquo;avait plutôt fait imaginer un processus collaboratif à la Wikipédia. Puis je me suis dit que puisque &amp;ldquo;ma&amp;rdquo; suite était connue depuis quelques années mais pas publiée dans l&amp;rsquo;OEIS, mes prédécesseurs avaient du se décourager, mais qu&amp;rsquo;il fallait que quelqu&amp;rsquo;un se sacrifie, alors voilà :&lt;/p&gt;
&lt;p&gt;2, 1, 1, 16, 8, 10, 15, 32, 36, 35, 2, 2, 17, 33, 13, 10, 15, 32, 36, 35, 17, 17, 9, 37, 7, 12, 6, &amp;hellip; telle est désormais ma suite d&amp;rsquo;entiers préférée.&lt;/p&gt;
&lt;p&gt;Et une référence &amp;ldquo;peer reviewed&amp;rdquo; de plus &lt;/p&gt;
\[2\]&lt;p&gt; !&lt;/p&gt;
&lt;h2 id="et-en-python-"&gt;Et en Python ?&lt;/h2&gt;
&lt;p&gt;Le code suivant, tiré de 
 , implante les trois suites mentionnées en Python, à l&amp;rsquo;aide de la
.&lt;/p&gt;
\[python\]&lt;p&gt; def dfs(n): return sum(map(factorial,digits(n)))&lt;/p&gt;
&lt;p&gt;A061602=Sequence(0, dfs, desc=&amp;ldquo;Sum of factorials of the digits of n&amp;rdquo;)&lt;/p&gt;
&lt;p&gt;A014080=Sequence(0,None,lambda n:dfs(n)==n)&lt;/p&gt;
&lt;p&gt;def dfcl(n): l=set() i=n while i not in l: l.add(i) i=dfs(i) return len(l)&lt;/p&gt;
&lt;p&gt;A303935=Sequence(0,dfcl) )&lt;/p&gt;
\[/python\]&lt;p&gt;Ce code est suffisant pour calculer quelques milliers de termes de A303935, mais pour résoudre le problème 74 rapidement, il faut être légèrement plus subtil&amp;hellip; Comme je ne donne pas de solutions aux Problèmes Euler, je vous laisse chercher&amp;hellip;&lt;/p&gt;
&lt;p&gt;En passant, ce problème était mon 100ème résolu, ce qui fait que je suis passé &amp;ldquo;Level 4 &amp;quot; !
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt=""
srcset="https://drgoulu.com/posts/2018/images/Dr_Goulu_hu_16796de24c6020d5.webp 200w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://drgoulu.com/posts/2018/images/Dr_Goulu_hu_16796de24c6020d5.webp"
width="200"
height="60"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="référence"&gt;Référence&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
\[altmetric doi="10.1017/S0025557200174996" float="right"\]Gupta, S. S. (2004). 88.31 Sum of the factorials of the digits of integers. The Mathematical Gazette, 88(512), 258–261.  DOI&amp;gt;
&lt;/li&gt;
&lt;li&gt;Ph. Guglielmetti, Sequence
 in &lt;em&gt;The On-Line Encyclopedia of Integer Sequences&lt;/em&gt; (2018), published electronically at 
.&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Suites infinies en Python</title><link>https://drgoulu.com/2017/06/26/series-infinies-et-oeis-en-python/</link><pubDate>Mon, 26 Jun 2017 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2017/06/26/series-infinies-et-oeis-en-python/</guid><description>&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt=""
srcset="https://drgoulu.com/posts/2017/images/E6fJJ_hu_3b7898a4ec4efc1d.webp 320w, https://drgoulu.com/posts/2017/images/E6fJJ_hu_85290121cecac221.webp 480w, https://drgoulu.com/posts/2017/images/E6fJJ_hu_a5739bbe4b0b5dde.webp 600w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://drgoulu.com/posts/2017/images/E6fJJ_hu_3b7898a4ec4efc1d.webp"
width="600"
height="500"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
Depuis que je programme en Python, j&amp;rsquo;entasse les petits bouts de code utiles ou potentiellement réutilisables dans &amp;ldquo;Goulib&amp;rdquo;, ma librairie perso et néanmoins disponible en open-source (licence LGPL)  sur
, 
, ReadTheDocs pour
, avec des
&lt;/p&gt;
&lt;p&gt;Comme la valeur d&amp;rsquo;un code se mesure surtout par les tests qui vérifient son bon fonctionnement, je me suis lancé dans un ambitieux projet (de plus&amp;hellip;) : tester un maximum de fonctions de mes modules
et
 en calculant des suites de l&amp;rsquo; 
, plus connue sous son acronyme anglais OEIS.&lt;/p&gt;
&lt;p&gt;Pour cela j&amp;rsquo;ai fait un truc dont je suis vachement fier* :&lt;/p&gt;
&lt;h2 id="la-classe-sequence"&gt;La classe Sequence&lt;/h2&gt;
&lt;p&gt;Ce
(
) permet de représenter une
(sequence en anglais) finie ou infinie de nombres** à partir d&amp;rsquo;&amp;lsquo;une ou plusieurs fonctions passées à
:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iterf : un
ou générateur produisant les termes de la suite l&amp;rsquo;un après l&amp;rsquo;autre&lt;/li&gt;
&lt;li&gt;itemf : une fonction  retournant le i-ème paramètre de la suite&lt;/li&gt;
&lt;li&gt;containf : une fonction renvoyant vrai (True) si son paramètre appartient à la suite.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On peut créer par exemple la série (infinie) des nombres premiers ainsi :&lt;/p&gt;
&lt;p&gt;
=Sequence(containf=
)&lt;/p&gt;
&lt;p&gt;Ensuite, l&amp;rsquo;objet ainsi créé avec n&amp;rsquo;importe quelle combinaison de ces 3 paramètres peut être utilisé indifféremment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;comme un générateur : for p in A000040() : print(p) # affiche les nombres premiers (jusqu&amp;rsquo;à l&amp;rsquo;infini)&lt;/li&gt;
&lt;li&gt;comme une liste: A000040
\[999\]# renvoie le 1000ème nombre premier (parce que Python indice à partir de 0&amp;hellip;)&lt;/li&gt;
&lt;li&gt;comme un ensemble: 201420142013 in A000040 # renvoie True car ce nombre est premier.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Evidemment, si on dispose de fonctions plus efficaces pour chacun de ces paramètres, on peut les utiliser pour rendre les calculs bien plus rapides. Par exemple pour la suite de Fibonacci, on peut exploiter trois algorithmes différents :&lt;/p&gt;
&lt;p&gt;
=Sequence(iterf=
, itemf=
, containf=
)&lt;/p&gt;
&lt;p&gt;En passant, voici comment est définie la séquence &amp;ldquo;auto descriptrice&amp;quot;qui orne la bannière de l&amp;rsquo;OEIS ci-dessus:&lt;/p&gt;
&lt;p&gt;
=Sequence( 
( lambda x: 
( 
( 
( 
( 
(x))))) ,1))&lt;/p&gt;
&lt;p&gt;Compact et presque clair, non ?&lt;/p&gt;
&lt;h2 id="dérivation"&gt;Dérivation&lt;/h2&gt;
&lt;p&gt;Une Sequence peut être dérivée d&amp;rsquo;une autre grâce à plusieurs méthodes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
renvoie une Sequence correspondant à la Sequence source filtrée par une fonction. Par exemple:
=A000040.filter(
) renvoie les &amp;ldquo;exposants de Mersenne&amp;rdquo;, les nombres premiers p tels que $2^p - 1$ est premier.&lt;/li&gt;
&lt;li&gt;
applique une fonction à chaque terme de la Sequence source. Par exemple la série des carrés des nombres premiers peut se définir ainsi:
=A000040.apply(lambda n:n*n)&lt;/li&gt;
&lt;li&gt;Pour éviter l&amp;rsquo;utilisation d&amp;rsquo;apply dans des cas trivialement simples, l&amp;rsquo;addition (et la soustraction) d&amp;rsquo;une Sequence et d&amp;rsquo;un entier sont définies. Par exemple:
=
+1 définit les
à partir des nombres de la forme $2^{2^n}$&lt;/li&gt;
&lt;li&gt;
renvoie une Sequence dont le n-ième terme est obtenu en sommant les n premiers termes de la Sequence source:
=A000040.accumulate()&lt;/li&gt;
&lt;li&gt;
renvoie une Sequence produite en appliquant applique une fonction à chaque paire de termes consécutifs. Ainsi la suite des différences entre nombres premiers consécutifs s&amp;rsquo;écrit:
=A000040.pairwise(operator.sub)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On peut aussi combiner deux Sequences pour en créer une nouvelle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;L&amp;rsquo;opérateur % (modulo, pourquoi pas&amp;hellip;) renvoie les termes de la Sequence opérande de gauche qui ne sont PAS dans l&amp;rsquo;opérande de droite. Ainsi:
=A000040 %
définit la Sequence des nombres premiers qui ne sont pas 
&lt;/li&gt;
&lt;li&gt;L&amp;rsquo;opérateur &amp;amp; (and) renvoie les termes de la Sequence opérande de gauche qui sont aussi dans l&amp;rsquo;opérande de droite. Ainsi:
=
&amp;amp; A000040 définit la suite des nombres premiers dont qui ne s&amp;rsquo;écrivent qu&amp;rsquo;avec des 0 et des 1.&lt;/li&gt;
&lt;li&gt;L&amp;rsquo;opérateur | (or) renvoie la fusion de deux séquences à l&amp;rsquo;aide du génial
. Ainsi:
=
|
définit les nombres ayant 4 diviseurs (dont 1 et eux-mêmes) comme l&amp;rsquo;union de ceux qui sont le produit de deux nombres premiers avec ceux qui sont le cube d&amp;rsquo;un nombre premier.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il y a encore deux méthodes un peu &amp;ldquo;tirées par les cheveux&amp;rdquo; du point de vue mathématique, mais qui fonctionnent très bien informatiquement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
 renvoie une Sequence
(par défaut) en triant les termes de la Sequence d&amp;rsquo;origine. Par exemple, voici la suites des surfaces des
 dans l&amp;rsquo;ordre croissant:
=Sequence(math2.primitive_triples).apply(lambda x:x
\[0\]*x
\[1\]//2) .sort()&lt;/li&gt;
&lt;li&gt;
 renvoie une Sequence où les termes redondants de la suite d&amp;rsquo;origine sont absents, par exemple:
 =
 .unique() // oui, parce que le 5 appartient à deux paires de nombres premiers jumeaux &amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ces méthodes utilisent respectivement 
 et 
 qui fonctionnent tant que la suite initiale est &amp;ldquo;presque&amp;rdquo; triée, à savoir tant que l&amp;rsquo;écart n entre un terme $a_i+n \leq a_i$ n&amp;rsquo;excède pas le paramètre &amp;ldquo;buffer&amp;rdquo; de la fonction. On peut évidemment trouver des suites qui ne pourront &amp;ldquo;mathématiquement&amp;rdquo; pas être traitées ainsi, mais informatiquement j&amp;rsquo;arrive à reproduire les suites de l&amp;rsquo;OEIS rencontrées jusqu&amp;rsquo;ici avec buffer=100 seulement&lt;/p&gt;
&lt;h2 id="le-dictionnaire-oeis"&gt;Le dictionnaire OEIS&lt;/h2&gt;
&lt;p&gt;J&amp;rsquo;ai ainsi constitué peu à peu le fichier 
 qui définit actuellement environ 150 séries de l&amp;rsquo;OEIS, et que j&amp;rsquo;étoffe lorsque l&amp;rsquo;occasion se présente. A la fin de ce module, j&amp;rsquo;utilise la géniale possibilité d&amp;rsquo;introspection de Python pour regrouper toutes les Sequence définies dans un dictionnaire fort utile pour les tests:&lt;/p&gt;
\[python\]&lt;p&gt;# Construit le dictionnaire OEIS par introspection : Simple et WOW ! oeis={} # on crée le dictionnaire vide seqs=globals().copy() # on copie tous les objets déclarés dans ce module for id in seqs: # on parcourt tous les identificateurs des objets if id&lt;/p&gt;
\[0\]&lt;p&gt;==&amp;lsquo;A&amp;rsquo; and len(id)==7: # et s&amp;rsquo;ils ont un nom de séquence OEIS &amp;hellip; oeis&lt;/p&gt;
\[id\]&lt;p&gt;=seqs&lt;/p&gt;
\[id\]&lt;p&gt; # on les ajoute au dictionnaire &amp;hellip; seqs&lt;/p&gt;
\[id\]&lt;p&gt;.name=id # et on définit leur champ name &lt;/p&gt;
\[/python\]&lt;h2 id="performance"&gt;Performance&lt;/h2&gt;
&lt;p&gt;Un point important est qu&amp;rsquo;aucun calcul n&amp;rsquo;est effectué lors des opérations ci-dessus. Ce n&amp;rsquo;est qu&amp;rsquo;au moment d&amp;rsquo;utiliser les Sequence ainsi définies que les fonctions iterf, itemf, containf et leurs éventuelles compositions sont évaluées.&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est un peu &amp;ldquo;automagique&amp;rdquo;, mais il faut tout de même veiller à utiliser les algorithmes les plus efficaces, et c&amp;rsquo;est même un des buts que je poursuis avec ce projet. Par exemple la suite des nombres premiers dont qui ne s&amp;rsquo;écrivent qu&amp;rsquo;avec des 0 et des 1
=
&amp;amp; A000040 aurait pu s’écrire également: A020449=A000040 &amp;amp; A007088 mais la première formulation est beaucoup plus rapide car la série A007088 des nombres ne s&amp;rsquo;écrivant qu&amp;rsquo;avec des 0 et des 1 croît beaucoup plus vite que celle des nombres premiers A000040. Il est par conséquent beaucoup plus rapide de tester si les nombres du genre 1101010101011101 sont premiers que d&amp;rsquo;énumérer tous les nombres premiers de 16 chiffres et de jeter ceux qui contiennent d&amp;rsquo;autres chiffres que des 0 ou 1.&lt;/p&gt;
&lt;p&gt;Malgré toutes ces précautions, parcourir une suite infinie prendra forcément un temps infini, et ces idiots d&amp;rsquo;ordinateurs ne sont toujours pas foutus de gérer le
. J&amp;rsquo;ai donc écrit un
 qui limite le temps de calcul d&amp;rsquo;une boucle ainsi :&lt;/p&gt;
&lt;p&gt;for x in
(suite,1): print(x) # affiche tous les termes calculables en 1 seconde (au total)&lt;/p&gt;
&lt;p&gt;Ce décorateur est utilisé en particulier dans
 la fonction appelée par 
 pour afficher les dix premiers termes d&amp;rsquo;une Sequence, ou du moins ceux qu&amp;rsquo;on peut calculer en moins d&amp;rsquo;une seconde.&lt;/p&gt;
&lt;p&gt;Les suites particulièrement lentes sont par exemple
ou 
 dont le n-ième terme donne le nombre de nombres inférieurs à 10^n  satisfaisant une certaine condition. Faute de meilleur algorithme, chaque terme prend 10x plus de temps que le précédent &amp;hellip;&lt;/p&gt;
&lt;h2 id="tests"&gt;Tests&lt;/h2&gt;
&lt;p&gt;Voià, tous les ingrédients sont prêts pour tester si mes beaux algorithmes fonctionnent correctement, ce qui était le but initial je le rappelle.&lt;/p&gt;
&lt;p&gt;Pour ceci, il suffit de vérifier que chaque Sequence fournit bien les mêmes termes que ceux disponibles dans l&amp;rsquo;OEIS. Ceci est facilité par le fait que l&amp;rsquo;OEIS fournit pour chaque suite les n premières valeurs dans un fichier texte en
accessible par le web. Par exemple 
 fournit les 10'000 premiers nombres premiers.&lt;/p&gt;
&lt;p&gt;Donc mon fichier 
 est lui aussi génialement simple* : il traverse le dictionnaire oeis et pour chaque Sequence il télécharge les N termes de référence du site OEIS, vérifie que les n premiers termes de la Sequence sont bien ceux attendus, et produit une erreur sinon. Ou un avertissement si itimeout a limité n à moins de 10.&lt;/p&gt;
&lt;p&gt;Ces tests sont automatiquement exécutés (avec beaucoup d&amp;rsquo;autres)
chaque fois que je pousse une nouvelle version de
, ce qui fait que je suis averti si modification crée un problème avec une ancienne version de Python ou si un Pingouin détecte un problème invisible de mes Fenêtres.&lt;/p&gt;
&lt;h2 id="débogage-de-loeis-"&gt;Débogage&amp;hellip; de l&amp;rsquo;OEIS !&lt;/h2&gt;
&lt;p&gt;J&amp;rsquo;ai ainsi beaucoup débogué, amélioré et optimisé la plupart des fonctions de  
et beaucoup d&amp;rsquo;
, et je compte poursuivre dans cette voie qui permet d&amp;rsquo;utiliser les données de l&amp;rsquo;OEIS comme une référence, validées par de nombreux utilisateurs.&lt;/p&gt;
&lt;p&gt;Sauf que des fois, le bug était chez eux. Et je ne suis pas peu fier d&amp;rsquo;
sur l&amp;rsquo;OEIS :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
 , comme déjà mentionné dans
, le 20ème terme de la série est 145, pas 142.&lt;/li&gt;
&lt;li&gt;
, dont certains termes avaient besoins de 0 supplémentaires. Tant qu&amp;rsquo;à faire j&amp;rsquo;ai fourni un B-file contenant les 1000 premiers termes exacts pour remplacer l&amp;rsquo;ancien qui n&amp;rsquo;en avait que 500.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A cette occasion j&amp;rsquo;ai remarqué que l&amp;rsquo;édition de l&amp;rsquo;OEIS est un peu rébarbative, ce qui peut décourager les contributeurs débutants. Peut-être que mon modeste projet pourrait servir de base à un site plus interactif, utilisant du code (Python of course) pour calculer dynamiquement les suites requises &amp;hellip;&lt;/p&gt;
&lt;p&gt;Ce long article mijoté pendant plus de six mois se termine par un appel aux allumés du code bonnes volontés : si cet article vous a motivé , n&amp;rsquo;hésitez pas à contribuer à la Goulib sur GitHub.&lt;/p&gt;
&lt;p&gt;Pour ma part je m&amp;rsquo;attaque aux
et
qui seront l&amp;rsquo;occasion de nettoyer le code du 
  en utilisant et testant le module 
&lt;/p&gt;
&lt;h3 id="notes"&gt;Notes&lt;/h3&gt;
&lt;p&gt;*sinon je n&amp;rsquo;en parlerais pas ici&amp;hellip; **ou d&amp;rsquo;autres objets mais je n&amp;rsquo;ai pas encore trouvé quoi&amp;hellip;&lt;/p&gt;</description></item><item><title>Comment calculer le 10'000'000'000'000'000'000 ème terme de la suite de Fibonacci</title><link>https://drgoulu.com/2017/04/25/comment-calculer-le-1e19-eme-terme-de-la-suite-de-fibonacci/</link><pubDate>Tue, 25 Apr 2017 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2017/04/25/comment-calculer-le-1e19-eme-terme-de-la-suite-de-fibonacci/</guid><description>&lt;p&gt;Tombé l&amp;rsquo;autre jour sur un problème idiot
mais intéressant : calculer le 1019 ème terme de la
. Idiot parce que ça ne sert à rien. Intéressant parce que ça sous-entend qu&amp;rsquo;il existe une manière de calculer le n-ième terme de cette suite définie par récurrence sans calculer tous les termes précédents. En effet, calculer les termes les uns après les autres prendrait dans les 300'000 ans à raison d&amp;rsquo;une microseconde par terme&amp;hellip;&lt;/p&gt;
&lt;h2 id="un-nombre-dor-mais-flottant"&gt;Un nombre d&amp;rsquo;or, mais flottant&lt;/h2&gt;
&lt;p&gt;Comme même les ésotéristes le savent, la suite de Fibonacci est liée au
 A partir de ce fait, Moivre, Euler et Binet ont indépendamment obtenu ce qu&amp;rsquo;on appelle aujourd&amp;rsquo;hui la
, et qui donne directement le n-ième terme de la suite:&lt;/p&gt;
&lt;p&gt;$\mathcal F_n=\frac1{\sqrt5}(\varphi^n-\varphi'^n)$, avec $ \varphi=\frac{1+\sqrt5}2$, et $ \varphi'=-\frac1\varphi$ .&lt;/p&gt;
&lt;p&gt;En pratique, le terme en $ \varphi'$ devient rapidement négligeable et il suffit de chercher l&amp;rsquo;entier le plus proche de $\frac{\varphi^n}{\sqrt5}$. Par exemple pour n=50, on peut calculer très vite $\mathcal F_{50}\approx\frac{\varphi^{50}}{\sqrt5}$ = 12586269025&lt;/p&gt;
&lt;p&gt;Mais on se heurte rapidement au problème de la précision de calcul en nombres flottants sur nos ordinateurs : $\varphi$ étant
(mais pas aussi
que $\pi$ &amp;hellip;), il faut calculer $\varphi^n$ avec au moins autant de décimales que $\mathcal F_n$ comporte de chiffres. Dès n=71 on dépasse les 53 bits de la mantisse des nombres &amp;ldquo;extended precision&amp;rdquo; et les dernières décimales des termes calculés sont faux.&lt;/p&gt;
&lt;p&gt;Dit autrement, à partir de n=70 la division $\mathcal F_n/\mathcal F_{n-1}$ donne au moins autant de décimales de $ \varphi$ que son calcul en nombres flottants&lt;/p&gt;
&lt;h2 id="la-matrice-salvatrice"&gt;La Matrice salvatrice&lt;/h2&gt;
&lt;p&gt;Pour une méthode de calcul en nombres entiers atteignant (presque) la rapidité de la formule de Binet, il faut utiliser l&amp;rsquo;idée d&amp;rsquo;un certain Brenner : écrire la récurrence de Fibonacci sous forme matricielle
. La matrice toute simple $Q=\begin{pmatrix}1&amp;1\\\\1&amp;0\end{pmatrix}$ permet d&amp;rsquo;obtenir un nouveau terme de la série de Fibonacci en la multipliant par un vecteur formé des deux termes précédents:&lt;/p&gt;
&lt;p&gt;$\begin{pmatrix}1&amp;1\\\\1&amp;0\end{pmatrix}\begin{pmatrix}\mathcal F_{n-1}\\\\\mathcal F_{n-2}\end{pmatrix}=\begin{pmatrix}\mathcal F_{n}\\\\\mathcal F_{n-1}\end{pmatrix}$&lt;/p&gt;
&lt;p&gt;En enchaînant les multiplications matricielles, on obtient le n-ième terme à partir des deux premiers (0,1) ainsi :&lt;/p&gt;
&lt;p&gt;$\begin{pmatrix}1&amp;1\\\\1&amp;0\end{pmatrix}^{n-1}\begin{pmatrix}1\\\\0\end{pmatrix}=\begin{pmatrix}\mathcal F_{n}\\\\\mathcal F_{n-1}\end{pmatrix}$&lt;/p&gt;
&lt;p&gt;En fait on retrouve les termes de la suite directement dans la matrice  $Q^n = \begin{pmatrix}\mathcal F_{n+1}&amp;\mathcal F_{n}\\\\\mathcal F_{n}&amp;\mathcal F_{n-1}\end{pmatrix}$.&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;algorithme de l&amp;rsquo;
permet d&amp;rsquo;élever la matrice Q à la puissance n en effectuant log2(n) multiplications de matrices 2x2, soit
pour n=1019. Ultra rapide, et facilement généralisable à d&amp;rsquo;autres formules de récurrence !&lt;/p&gt;
&lt;p&gt;Malheureusement, la fonction
 souffre d&amp;rsquo;une
qui empêche de l&amp;rsquo;utiliser dès n=71 car elle utilise les nombres flottants. Il m&amp;rsquo;a donc fallu la réécrire, en résolvant une petite contrainte technologique au passage.&lt;/p&gt;
&lt;h2 id="vers-les-pétaoctets-et-au-delà-"&gt;Vers les pétaoctets et au delà &amp;hellip;&lt;/h2&gt;
&lt;p&gt;Il se trouve que le 1019-ième terme de la suite de Fibonacci est trop grand pour tenir dans le plus super des ordinateurs. En effet, la longueur du n-ième terme de la suite s&amp;rsquo;obtient facilement en prenant le log (base 10)  de la formule de Binet : $\mathcal L_n=\lceil n\log{\varphi}\rceil$ , soit grosso-modo n*0.208987640249978733769272089237555416822459239918210953539&amp;hellip;&lt;/p&gt;
&lt;p&gt;Vérifions. Pour n=1000, on obtient:&lt;/p&gt;
&lt;p&gt;$\mathcal F_{1000}$=43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875 qui comporte bien pile 209 chiffres.&lt;/p&gt;
&lt;p&gt;Donc $\mathcal F_{10^{19}}$ comporte 2089876402499787337 chiffres &amp;hellip; Il faudrait 
 
octets de RAM (de préférence&amp;hellip;) pour stocker ce nombre &amp;hellip;&lt;/p&gt;
&lt;figure class="alignright" style="max-width: 500px; width: 100%;"&gt;&lt;img src="https://drgoulu.com/posts/2017/images/1nv66i_hu_7b93cc781b3ade88.webp" srcset="https://drgoulu.com/posts/2017/images/1nv66i_hu_7b93cc781b3ade88.webp 320w, https://drgoulu.com/posts/2017/images/1nv66i_hu_b71a7d5dd930ae.webp 480w, https://drgoulu.com/posts/2017/images/1nv66i_hu_8e32ab0b43c8315.webp 500w" sizes="(max-width: 640px) 100vw, 500px" alt="(mon premier meme ... désolé ...)" loading="lazy" class="rounded-lg shadow-sm w-full h-auto" data-zoomable /&gt;&lt;figcaption&gt;(mon premier meme &amp;hellip; désolé &amp;hellip;)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="et-modulo1000000007-"&gt;Et modulo 1000000007 ?&lt;/h2&gt;
&lt;p&gt;Fort heureusement, le problème idiot avait un petit détail en prime : il fallait calculer le résultat
1000000007, et ça, ça change tout.&lt;/p&gt;
&lt;p&gt;Ce nombre est premier (pour que ça soit plus intéressant), mais surtout il est inférieur à 230, ce qui fait que la multiplication modulo 1000000007 ne nécessite que des opérations sur des 
32 bits (signés), ultra rapide.&lt;/p&gt;
&lt;p&gt;Voici donc quelques fonctions Python tirées du
 qui effectuent:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le produit matriciel, optionnellement modulaire&lt;/li&gt;
&lt;li&gt;l&amp;rsquo;exponentiation rapide d&amp;rsquo;une matrice, optionnellement modulaire, et qui corrige le bug de numpy.matrix_power&lt;/li&gt;
&lt;li&gt;le calcul quasi instantané du n-ième terme de la série de Fibonacci, avec une option modulaire vivement recommandée pour de grands n&lt;/li&gt;
&lt;/ul&gt;
\[gist id="f56725fa32fbb4840c855a8309662c9d"\]&lt;p&gt;Avec ça on obtient fibonacci(int(1E19),1000000007) = 647754067 ce qui nous fait une belle jambe,  mais nous a appris pas mal de choses intéressantes, non ?&lt;/p&gt;
&lt;p&gt;Prochaine étape : trouver la
correspondante &amp;hellip;&lt;/p&gt;
&lt;h3 id="références"&gt;Références&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span id="ref-1"&gt;&lt;/span&gt;
 sur StackOverflow (et
qui m&amp;rsquo;a inspiré cet article)&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-2"&gt;&lt;/span&gt;Weisstein, Eric W. &amp;ldquo;
.&amp;rdquo; From MathWorld&amp;ndash;A Wolfram Web Resource.&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-3"&gt;&lt;/span&gt;Arthur Charpentier &amp;ldquo;
, 2016 sur Freakonometrics (mention spéciale pour l&amp;rsquo;image de la spirale&amp;hellip;)&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>2017 et les triplets pythagoriciens</title><link>https://drgoulu.com/2017/01/02/2017-et-les-triplets-pythagoriciens/</link><pubDate>Mon, 02 Jan 2017 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2017/01/02/2017-et-les-triplets-pythagoriciens/</guid><description>&lt;p&gt;Au moment d&amp;rsquo;envoyer un mot à Franck à propos de son livre
, je me suis rappelé qu&amp;rsquo;il était à l&amp;rsquo;origine de 
 sur la pédagogie appliquée au théorème de Pythagore (et aussi de 
 sur la Relativité).  J&amp;rsquo;ai ainsi découvert celle des 
 sur laquelle baser mes vœux pour cette nouvelle année :&lt;/p&gt;
&lt;h2 id="bonne-et-heureuse-année--"&gt;Bonne et Heureuse Année $\sqrt{792^2+1855^2}$ !&lt;/h2&gt;
&lt;p&gt;En effet, 2017 est un &amp;ldquo;nombre hypotenuse&amp;rdquo; (
), l&amp;rsquo;hypoténuse du triangle rectangle correspondant au
(792,1855,2017). C&amp;rsquo;est même un triplet pythagoricien &amp;ldquo;primitif&amp;rdquo; car ces 3 entiers sont
, donc 2017 est un &amp;ldquo;nombre hypoténuse primitif&amp;rdquo; (
).&lt;/p&gt;
&lt;figure class="alignright" style="max-width: 312px; width: 100%;"&gt;&lt;img src="https://drgoulu.com/posts/2017/images/artmaths0202.gif" alt="(3,4,5), le premier et le plus célèbre des triplets pythagoriciens" loading="lazy" class="rounded-lg shadow-sm w-full h-auto" /&gt;&lt;figcaption&gt;(3,4,5), le premier et le plus célèbre des triplets pythagoriciens&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Car comme on le voit dans
, il existe deux sortes de triplets pythagoriciens: les
et les composés, qui sont des multiples de triplets plus petits, par exemple (6,8,10) obtenu en doublant (3,4,5). Ca va avoir de l&amp;rsquo;importance plus bas.&lt;/p&gt;
&lt;h3 id="comment-générer-les-triplets-pythagoriciens"&gt;Comment générer les triplets pythagoriciens&lt;/h3&gt;
&lt;p&gt;Il existe plusieurs algorithmes simples pour générer des triplets pythagoriciens. Ils sont tous mauvais. Ceux qui utilisent deux boucles imbriquées voire trois sont lents, ceux qui calculent la racine carrée en nombres flottants sont imprécis, et tous sont limités par une longueur maximale des côtés recherchés. 
écrit pour résoudre 
du
 ne faisait pas exception.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai depuis découvert l&amp;rsquo;existence d&amp;rsquo;une méthode beaucoup plus efficace, mais pas simple. Quelques millénaires après
et la
, en 1934 pour être précis, un matheux nommé Berggren a démontré que chaque triplet primitif pouvait être généré à partir de (3,4,5) en le multipliant par une combinaison de ces 3 petites matrices
:&lt;/p&gt;
&lt;p&gt;$R_1 =\begin{pmatrix} 1 &amp; -2 &amp; 2 \\\\ 2 &amp; -1 &amp; 2 \\\\ 2 &amp; -2 &amp; 3 \\\\ \end{pmatrix}$ $R_2 =\begin{pmatrix} 1 &amp; 2 &amp; 2 \\\\ 2 &amp; 1 &amp; 2 \\\\ 2 &amp; 2 &amp; 3 \\\\ \end{pmatrix}$ $R_3 =\begin{pmatrix} -1 &amp; 2 &amp; 2 \\\\ -2 &amp; 1 &amp; 2 \\\\ -2 &amp; 2 &amp; 3 \\\\ \end{pmatrix}$&lt;/p&gt;
&lt;p&gt;Un autre matheux nommé Barning ayant montré en 1963** que chaque triplet n&amp;rsquo;est ainsi produit que d&amp;rsquo;une et une seule manière
, il suffit de parcourir l&amp;rsquo;
formé en multipliant chaque nœud par chacune de ces 3 matrices pour générer l&amp;rsquo;un après l&amp;rsquo;autre tous les triplets primitifs:&lt;/p&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt=""
srcset="https://drgoulu.com/posts/2017/images/1000px-Pythagorean.tree_.svg__hu_64a74985c05164a7.webp 320w, https://drgoulu.com/posts/2017/images/1000px-Pythagorean.tree_.svg__hu_8a349dc5e68e895.webp 480w, https://drgoulu.com/posts/2017/images/1000px-Pythagorean.tree_.svg__hu_940acb0a6d182fab.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://drgoulu.com/posts/2017/images/1000px-Pythagorean.tree_.svg__hu_64a74985c05164a7.webp"
width="760"
height="698"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Ensuite, un autre bout de code peut multiplier chaque triplet primitif par 2,3,4,&amp;hellip; pour ajouter les triplets composés et produire ainsi tous les triplets pythagoriciens l&amp;rsquo;un après l&amp;rsquo;autre. C&amp;rsquo;est ce que fait très efficacement ce
.&lt;/p&gt;
&lt;h3 id="oui-mais-dans-lordre-svp"&gt;Oui mais dans l&amp;rsquo;ordre svp&lt;/h3&gt;
&lt;p&gt;Une difficulté supplémentaire apparaît si on veut obtenir les triplets triés dans un ordre croissant, par exemple de leur hypoténuse, puis du long côté comme dans
 ou dans 
 et d&amp;rsquo;autres séries de l&amp;rsquo;OEIS. Evidemment, si on ne s&amp;rsquo;intéresse qu&amp;rsquo;aux N premiers triplets, on peut toujours les générer, et les trier ensuite. Mais si on veut chercher par exemple le plus petit triplet dont la longueur de hypoténuse est un carré ainsi que la somme des côtés*, on ne  connait a priori pas N&amp;hellip;&lt;/p&gt;
&lt;p&gt;Pour générer les triplets dans un ordre croissant, j&amp;rsquo;ai incorporé le tri aux fonctions précédentes à l&amp;rsquo;aide de
, mais je ne vais pas accentuer votre mal de cheveux annuel en vous détaillant ça ici. Pour ceux que ça intéresse,
de ma librairie Goulib dont je vous reparlerai très bientôt, et
où vous pouvez voter pour moi&amp;hellip;&lt;/p&gt;
&lt;p&gt;Mais bon, juste parce que j&amp;rsquo;en suis assez content, en testant mon code j&amp;rsquo;ai découvert une erreur dans 
 : le 20ème terme de la série est 145, pas 142. Et j&amp;rsquo;ai ainsi pu constater que l&amp;rsquo;OEIS est un bon site scientifique car il est doté d&amp;rsquo;un système rigoureux mais efficace de correction des erreurs.&lt;/p&gt;
&lt;p&gt;Pour terminer, à tous ceux qui se demandent à quoi peut bien servir tout ça, je recommande la lecture de
auquel je n&amp;rsquo;ai pas tout compris (séquelles du champagne je suppose), mais qui propose une nouvelle méthode de cryptographie basée sur les triplets pythagoriciens.&lt;/p&gt;
&lt;p&gt;Comme quoi les années passent, mais les bonnes idées restent et construisent inéluctablement un monde plus intéressant à découvrir.&lt;/p&gt;
&lt;p&gt;Mes chers lecteurs, je vous souhaite à tous l&amp;rsquo;indispensable Santé, le très important Bonheur, et l&amp;rsquo;Etonnement qu&amp;rsquo;apportent la curiosité, la découverte et la compréhension de ce monde étrange.&lt;/p&gt;
&lt;h3 id="notes"&gt;Notes:&lt;/h3&gt;
&lt;p&gt;* C&amp;rsquo;est (4565486027761, 1061652293520, 4687298610289=21650172), découvert en 1643 par
.
 (edit du 9.1.2016 :) dont je n&amp;rsquo;ai aucune idée de comment il a fait à l&amp;rsquo;époque spectaculaires, d&amp;rsquo;autant que la recherche avec le générateur proposé dans cet article prend des plombes&amp;hellip; 
vaut décidément bien mieux, et en plus indique 
 .&lt;/p&gt;
&lt;p&gt;** Excellente année aussi&amp;hellip;&lt;/p&gt;
&lt;h3 id="références"&gt;Références:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span id="ref-1"&gt;&lt;/span&gt;&lt;span class="openbook_template5"&gt;&lt;a href="http://openlibrary.org/authors/OL3958741A/Franck_Le_Vallois" title="Click to view author in Open Library" target="_blank" rel="noopener"&gt;Franck Le Vallois&lt;/a&gt; (2016). &lt;a href="http://openlibrary.org/books/OL26208349M/L'expérience_humaine" title="Click to view title in Open Library" target="_blank" rel="noopener"&gt;L'expérience humaine: Regard sur une métamorphose inachevée&lt;/a&gt;. Gouvernances.&lt;span class="Z3988" title="ctx_ver=Z39.88-2004&amp;amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;amp;rft.genre=book&amp;amp;rft.btitle=L%27exp%C3%A9rience+humaine%3A+Regard+sur+une+m%C3%A9tamorphose+inachev%C3%A9e&amp;amp;rft.isbn=9782839919647&amp;amp;rft.au=Franck+Le+Vallois&amp;amp;rft.pub=Gouvernances&amp;amp;rft.date=2016"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-2"&gt;&lt;/span&gt;Berggren, &amp;ldquo;Pytagoreiska trianglar&amp;rdquo;, 1934, Tidskrift för elementär matematik, fysik och kemi, vol. 17, p. 129-139.&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-3"&gt;&lt;/span&gt;Barning, F. J. M. &amp;ldquo;Over pythagorese en bijna-pythagorese driehoeken en een generatieproces met behulp van unimodulaire matrices&amp;rdquo;, 1963 Math. Centrum Amsterdam Afd. Zuivere Wisk. ZW-011: 37, (
)&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-4"&gt;&lt;/span&gt;Spezeski, W. J. &amp;ldquo;
&amp;rdquo;, 2008, Applications and Applied Mathematics: An International Journal, 3(1), 100–112. Retrieved from
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Couleurs, Gamuts, Python et Open Source</title><link>https://drgoulu.com/2016/09/30/couleur-gamut-python-open-source/</link><pubDate>Fri, 30 Sep 2016 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2016/09/30/couleur-gamut-python-open-source/</guid><description>&lt;p&gt;Ce fut une excellente journée de travail, stimulante et productive. Tôt le matin, Cédric m&amp;rsquo;a montré les slides d&amp;rsquo;un article sur la mesure géométrique de la différence entre deux 
, en me demandant s&amp;rsquo;il était facile de programmer la méthode présentée
&lt;/p&gt;
&lt;p&gt;Avant d&amp;rsquo;attaquer la question et la réponse,  une petite introduction sur le merveilleux monde des couleurs s&amp;rsquo;impose.&lt;/p&gt;
&lt;h3 id="couleurs-et-gamuts"&gt;Couleurs et gamuts&lt;/h3&gt;
&lt;p&gt;En imprimerie, on mesure à l&amp;rsquo;aide d&amp;rsquo;un
 l&amp;rsquo;ensemble des couleurs produites par une imprimante, ne serait-ce que pour la calibrer 
. Mais lorsqu&amp;rsquo;on développe une imprimante industrielle, il faut en plus mesurer l&amp;rsquo;effet sur les couleurs de nombreux paramètres (qualité des substrats, composition des encres, puissance des séchoirs etc. ) afin de maximiser le &amp;ldquo;volume&amp;rdquo; des couleurs que l&amp;rsquo;imprimante est capable de reproduire.&lt;/p&gt;
&lt;figure class="alignright" style="max-width: 320px; width: 100%;"&gt;&lt;img src="https://drgoulu.com/posts/2016/images/color7.gif" alt="cube des couleurs RGB (Red Green Blue)" loading="lazy" class="rounded-lg shadow-sm w-full h-auto" /&gt;&lt;figcaption&gt;cube des couleurs RGB (Red Green Blue)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;L&amp;rsquo;ensemble des couleurs définit un volume car il faut trois paramètres pour déterminer une couleur. La représentation la plus connue est celle basée sur les
rouge, vert et bleu, le &amp;ldquo;RGB&amp;rdquo;. Sur votre écran, des pixels rouges, verts et bleus peuvent être allumés avec des intensités variables, habituellement codée par un entier entre 0 et 255. La
permet ainsi de vous faire percevoir 16'777'216 couleurs différentes définies par autant de points dans le cube ci-contre.&lt;/p&gt;
&lt;p&gt;De même, on peut faire un cube &amp;ldquo;CMY&amp;rdquo; avec les couleurs produites par
en déposant des encres cyan, magenta et jaune sur du papier supposé blanc.&lt;/p&gt;
&lt;p&gt;Oui mais voilà, comme vous vous en apercevez chaque fois que vous imprimez des photos à la maison, on n&amp;rsquo;obtient pas les mêmes couleurs en RGB et en CMY. Les deux cubes ne se superposent pas bien. Intuitivement, on comprend qu&amp;rsquo;un écran ne peut pas être plus noir que quand il est éteint, et qu&amp;rsquo;on ne peut pas rendre le papier plus blanc qu&amp;rsquo;il ne l&amp;rsquo;est originellement. Le même raisonnement s&amp;rsquo;applique aux autres directions de l&amp;rsquo;espace des couleurs : certaines couleurs possibles en RGB ne peuvent pas être obtenues en impression CMY, et vice-versa. C&amp;rsquo;est d&amp;rsquo;ailleurs pour ça que les imprimantes photo ont des couleurs supplémentaires, souvent du &amp;ldquo;magenta clair&amp;rdquo; et du &amp;ldquo;cyan clair&amp;rdquo; pour mieux imprimer vos couchers de soleils. Et aussi du noir, mais c&amp;rsquo;est plutôt pour imprimer du plus joli texte et économiser de l&amp;rsquo;encre des 3 autres couleurs.&lt;/p&gt;
&lt;p&gt;Les couleurs que l&amp;rsquo;on peut reproduire fidèlement se trouvent dans l&amp;rsquo;intersection des deux cubes, mais les axes des deux cubes ne sont pas les mêmes, alors comment faire ?&lt;/p&gt;
&lt;p&gt;En les représentant dans un autre espace de couleurs, plus vaste, le 
. Dans cet espace, la coordonnée L* correspond à la
et les coordonnées a* et b* à des échelles entre couleurs tenant compte de la sensibilité de l&amp;rsquo;oeil humain. En LAB, le cube CMY d&amp;rsquo;une imprimante donnée devient un patatoïde comme celui représenté ci-dessous, et le cube RGB d&amp;rsquo;un écran précis devient le volume enfermé dans le treillis. Comme on le voit, l&amp;rsquo;intersection des deux est un volume compliqué, nettement plus petit que chacun des patatoïdes.&lt;/p&gt;
&lt;figure class="aligncenter" style="max-width: 512px; width: 100%;"&gt;&lt;a href="http://www.viegroup.com/mvrhelweb/gamut.html" target="_blank" rel="noopener"&gt;&lt;img src="https://drgoulu.com/posts/2016/images/crt_print_gamut_big_hu_b8550179eb21b5a1.webp" srcset="https://drgoulu.com/posts/2016/images/crt_print_gamut_big_hu_b8550179eb21b5a1.webp 320w, https://drgoulu.com/posts/2016/images/crt_print_gamut_big_hu_727fb52fba9820d1.webp 480w, https://drgoulu.com/posts/2016/images/crt_print_gamut_big_hu_3147257836a9c584.webp 512w" sizes="(max-width: 640px) 100vw, 512px" alt="Gamut d" loading="lazy" class="rounded-lg shadow-sm w-full h-auto" data-zoomable /&gt;&lt;/a&gt;&lt;figcaption&gt;Gamut d&amp;rsquo;une imprimante (solide) et d&amp;rsquo;un moniteur (treillis). Image : Michael J. Vrhel&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3 id="retour-aucode"&gt;Retour au Code&lt;/h3&gt;
&lt;p&gt;Bon mais alors, est-ce qu&amp;rsquo;on peut faire facilement un programme qui calcule l&amp;rsquo;intersection de deux gamuts quelconques ? Dans ces cas là la réponse standard est &amp;ldquo;ça dépend&amp;rdquo; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;si le code Matlab décrit dans l&amp;rsquo;article
est disponible, c&amp;rsquo;est tout cuit. Et peut-être même gratuit si  
 suffit.&lt;/li&gt;
&lt;li&gt;sinon (je n&amp;rsquo;ai pas trouvé le code&amp;hellip;), ça dépend de l&amp;rsquo;existence de librairies (Python de préférence) effectuant les opérations délicates:
&lt;ul&gt;
&lt;li&gt;Lire les fichiers produits par le  : pas de problème car ce sont des .txt avec les valeurs LAB des quelques dizaines de couleurs mesurées&lt;/li&gt;
&lt;li&gt;Obtenir l&amp;rsquo;
* des points : pas de problème non plus. J&amp;rsquo;avais déjà utilisé la fonction 
 basée sur la librairie 
. C&amp;rsquo;est d&amp;rsquo;ailleurs la même qu&amp;rsquo;utilise la fonction Matlab 
. Et elle fournit même le volume du
en prime!&lt;/li&gt;
&lt;li&gt;Par contre, je n&amp;rsquo;ai aucune idée de comment calculer l&amp;rsquo;intersection des deux polyèdres / gamuts. Ou plutôt si : je sais que si je ne trouve pas du code faisant ça vite et bien, je vais passer longtemps à le faire mal. En gros il faut calculer l&amp;rsquo;intersection de la surface en fil de fer avec la surface colorée dans l&amp;rsquo;image ci-contre, et vice-versa. L&amp;rsquo;algorithme le plus simple qui vient à l&amp;rsquo;esprit est de vérifier si chaque côté des petits triangles formant une surface intersecte un (ou deux &amp;hellip;) triangles formant l&amp;rsquo;autre surface, puis de refaire une enveloppe convexe de tous points d&amp;rsquo;intersection. Comme tout algo simple, il y en a probablement un bien plus efficace quelque part &amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En cherchant un peu, je tombe sur la solution :
, un package Python capable de faire des &amp;ldquo;Boolean operations on meshes (intersection, union, difference)&amp;rdquo;. Je l&amp;rsquo;installe, je code le gros de l&amp;rsquo;application, et exactement une heure après avoir attaqué le problème, je vois le volume de mes gamuts s&amp;rsquo;afficher ! mais l&amp;rsquo;appel à la fonction calculant l&amp;rsquo;intersection crashe&amp;hellip;&lt;/p&gt;
&lt;h3 id="cest-la-saison-des-châtaignes-débogage"&gt;C&amp;rsquo;est la saison des châtaignes : débogage&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;d&amp;rsquo;abord il faut installer
, un wrapper Python de 
. J&amp;rsquo;avais déjà rencontré cette librairie permettant de trouver rapidement les points les plus proches d&amp;rsquo;un autre en utilisant la structure d&amp;rsquo;
mais n&amp;rsquo;étais pas sur d&amp;rsquo;en avoir vraiment besoin. Un message d&amp;rsquo;erreur me dit que si&amp;hellip; Sur une machine Windows, cette librairie ne s&amp;rsquo;installe pas &amp;ldquo;toute seule&amp;rdquo;, il faut aller la chercher sur la fabuleuse page &amp;ldquo;
&amp;rdquo;&lt;/li&gt;
&lt;li&gt;ensuite je découvre pourquoi la phrase &amp;ldquo;Boolean operations on meshes (intersection, union, difference)&amp;rdquo; avait une suite : &amp;ldquo;if
or
is installed&amp;rdquo;. En fait, trimesh appelle l&amp;rsquo;un ou l&amp;rsquo;autre de ces logiciels pour faire le vrai boulot. Connaissant l&amp;rsquo;existence des deux, mais ayant joué avec Blender il y a quelques années et me souvenant qu&amp;rsquo;il utilise Python comme langage de script, j&amp;rsquo;opte pour Blender et &amp;hellip;&lt;/li&gt;
&lt;li&gt;&amp;hellip; le code tourne ! Mais me renvoie toujours un volume de l&amp;rsquo;intersection de mes gamuts nul. Ce sont les
temporaires que trimesh crée qui sont encore ouverts lorsque Blender y accède. Bizarre, parce que
 pour effectuer des tests automatiques et que ceux-ci ne montrent aucun problème &amp;hellip; Mais les tests tournent sur une machine (virtuelle) Linux ! Apparemment, un pingouin a le droit de lire un fichier qu&amp;rsquo;un autre pingouin a laissé ouvert, mais une fenêtre doit être refermée avant qu&amp;rsquo;elle puisse être réouverte&amp;hellip; Je vais devoir modifier le code de trimesh pour corriger ça, et je décide de le faire bien.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="lopen-source-cest-bon-pour-le-business"&gt;L&amp;rsquo;Open Source, c&amp;rsquo;est bon pour le business&lt;/h3&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="%image_alt%"
srcset="https://drgoulu.com/posts/2016/images/opensourceloveday_hu_f8e8269531be077.webp 320w, https://drgoulu.com/posts/2016/images/opensourceloveday_hu_14d395d074fc5b5c.webp 480w, https://drgoulu.com/posts/2016/images/opensourceloveday_hu_2ebfd42e89b313b1.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://drgoulu.com/posts/2016/images/opensourceloveday_hu_f8e8269531be077.webp"
width="760"
height="428"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
En effet, je ne sais pas si vous avez tout suivi mais je travaille dans une entreprise qui vend des machines pour gagner du pognon et payer quelques salaires en passant. Or tous les logiciels utilisés pour développer cette application relativement pointue en une seule journée sont &amp;ldquo;
&amp;rdquo;, gratuits. La moindre des choses est, me semble-t-il, de renvoyer parfois l&amp;rsquo;ascenseur partageant les améliorations que j&amp;rsquo;apporte à ces logiciels, évidemment dans la mesure où ça ne va pas à l&amp;rsquo;encontre des intérêts de mon employeur.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai donc
de 
, modifié
qui gère les fichiers temporaires et hop ! l&amp;rsquo;application fonctionne après grosso-modo 4 heures de boulot et 4 cafés. Et comme
 de trimesh passent toujours (sur Linux), je soumets une &amp;ldquo;
&amp;rdquo; à l&amp;rsquo;auteur de trimesh pour lui proposer d&amp;rsquo;incorporer mes changements à la branche officielle : B.A. accomplie en une heure supplémentaire à tout casser.&lt;/p&gt;
&lt;p&gt;Après ça il me restait encore un moment pour implanter une idée qui m&amp;rsquo;avait traversé l&amp;rsquo;esprit : compter combien de
sont incluses dans chaque gamut (et donc possible d&amp;rsquo;imprimer avec notre machine), et lister celles qui n&amp;rsquo;y sont pas. Mais comme les valeurs Lab de ces couleurs sont protégées par copyright, je ne pourrai pas mettre ce code à disposition en Open Source. Des centaines de milliers de lignes de code testé et performant oui, mais une table de couleurs, non&amp;hellip;&lt;/p&gt;
&lt;p&gt;Note* : ce qu&amp;rsquo;il y a de bien en écrivant un article, c&amp;rsquo;est que ça met les idées en place, ou que ça instille un léger doute. En regardant les polyèdres des gamuts RGB et CMY dans LAB, on voit qu&amp;rsquo;ils ne sont pas convexes&amp;hellip; Pour être plus précis, peut-être bien qu&amp;rsquo;il me faudrait utiliser un algo plus subtil que ConvexHull &amp;hellip;&lt;/p&gt;
&lt;h3 id="références"&gt;Références:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span id="ref-1"&gt;&lt;/span&gt;Deshpande, K., Green, P., &amp;amp; Pointer, M. R. &amp;ldquo;Metrics for comparing and analyzing two colour gamuts&amp;rdquo;, 2015, Color Research &amp;amp; Application, 40(5), 465–471.
(
)&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-2"&gt;&lt;/span&gt;Arnaud Frich &amp;ldquo;Guide de la Gestion des Couleurs -
&amp;rdquo;, 2016&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-3"&gt;&lt;/span&gt;Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., &amp;ldquo;The Quickhull algorithm for convex hulls,&amp;rdquo; &lt;em&gt;ACM Trans. on Mathematical Software&lt;/em&gt;, 22(4):469-483, Dec 1996,
&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-4"&gt;&lt;/span&gt;Vrhel, M. J., &amp;amp; Trussell, H. J. &amp;ldquo;
&amp;rdquo;, 1999, IEEE Transactions on Image processing, 8(12).&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-5"&gt;&lt;/span&gt;Beckmann, N., &amp;amp; al. &amp;ldquo;
&amp;rdquo; 1990, In Proceedings of the 1990 ACM SIGMOD international conference on Management of data  - SIGMOD ’90 (Vol. 19, pp. 322–331). New York, USA: ACM Press. DOI : 
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>"Contre-exemples" au théorème de Fermat-Wiles</title><link>https://drgoulu.com/2016/03/25/contre-exemples-au-theoreme-de-fermat-wiles/</link><pubDate>Fri, 25 Mar 2016 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2016/03/25/contre-exemples-au-theoreme-de-fermat-wiles/</guid><description>&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;&lt;img alt="simpson_fermat"
src="https://drgoulu.com/posts/2016/images/simpson_fermat.gif"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
vient de remporter le
pour sa démonstration du
qui dit qu’il n’existe pas de solution de l’équation an+bn=cn pour a,b,c,n entiers et n&amp;gt;2. Pourtant &lt;em&gt;&lt;mauvaise foi=on&gt;&lt;/em&gt; :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;il a en réalité &amp;ldquo;seulement&amp;rdquo; démontré un cas particulier du 
(aussi appelé conjecture de Shimura-Taniyama-Weil) dont le théorème de Fermat résulte directement&lt;/li&gt;
&lt;li&gt;quelques semaines après la publication des quelques 100 pages de la démonstration d&amp;rsquo;Andrew Wiles en 1995
, Homer Simpson se promène nonchalamment et en 3D devant un contre-exemple : 1782¹² + 1841¹² = 1922¹²
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Cette égalité est due à 
, matheux et co-scénariste de cette
. Si on la vérifie sur une calculatrice standard, on trouve que le terme de gauche vaut 2.541210259e+39 et que celui de droite vaut&amp;hellip; 2.541210259e+39 ! C&amp;rsquo;est un contre-exemple du Grand théorème de Fermat, et la démonstration d&amp;rsquo;Andrew Wiles ne vaut pas tripette! &lt;em&gt;&lt;mauvaise foi=off&gt;&lt;/em&gt; &lt;/p&gt;
&lt;p&gt;Mais en fait non:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Avec plus de décimales ou Python, on voit que le terme de gauche vaut 2541210258614589176288669958142428526657 et celui de droite 2541210259314801410819278649643651567616. Ca fait une gigantesque différence de 700212234530608691501223040959, mais comme elle représente moins d&amp;rsquo;un milliardième des nombres précédents (2.75 10-10 pour être précis), une bête calculatrice du siècle passé qui ne calculait qu&amp;rsquo;avec 9 chiffres significatifs ou en nombres flottants sur 32 bits pouvait considérer ce chiffre comme comparativement négligeable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;figure class="alignright" style="max-width: 420px; width: 100%;"&gt;&lt;img src="https://drgoulu.com/posts/2016/images/18-Homer-Simpson_hu_367090b8e829538f.webp" srcset="https://drgoulu.com/posts/2016/images/18-Homer-Simpson_hu_367090b8e829538f.webp 320w, https://drgoulu.com/posts/2016/images/18-Homer-Simpson_hu_5280899a76f54864.webp 480w, https://drgoulu.com/posts/2016/images/18-Homer-Simpson_hu_722eb58d6bcdf88a.webp 620w" sizes="(max-width: 640px) 100vw, 420px" alt="quatre grands problèmes scientifiques résolus sur un seul tableau !" loading="lazy" class="rounded-lg shadow-sm w-full h-auto" data-zoomable /&gt;&lt;figcaption&gt;quatre grands problèmes scientifiques résolus sur un seul tableau !&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;En y regardant de plus près, on voit tout de suite que l&amp;rsquo;égalité est fausse car comme 1782 est pair, 1782¹² l&amp;rsquo;est aussi et comme 1841¹² est impair pour les mêmes raisons, la somme 782¹² + 1841¹² est impaire. Or 1922¹²  est pair&amp;hellip;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Informé de cette dernière objection, David X. Cohen a fait apparaître dans un autre épisode
le tableau noir suivant, sur lequel se trouve un autre faux contre-exemple : 3987¹² + 4365¹² = 4472¹². Belle conscience professionnelle, n&amp;rsquo;est-ce pas ?*&lt;/p&gt;
&lt;p&gt;Cette fois l&amp;rsquo;égalité proposée respecte la parité, et est &amp;ldquo;moins fausse&amp;rdquo; que la précédente puisque la différence ne représente que 1.89 10-11 des termes.&lt;/p&gt;
&lt;p&gt;Je me suis alors posé trois questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt; y&amp;rsquo;a-t-il beaucoup de tels contre-exemples ?&lt;/li&gt;
&lt;li&gt;y&amp;rsquo;a-t-il une raison particulière au fait  que les deux exemples de David X. Cohen utilisent la puissance 12 ?&lt;/li&gt;
&lt;li&gt;peut-on en trouver qui sont encore moins faux que 1.89 10-11 ?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;J&amp;rsquo;ai donc écrit un
** qui génère les triplets (a,b,c) tels que an+bn=cn est juste à 10-9 près, en balayant les nombres a et b entre 100 et 100'000 et incrémentant n à partir de 3. Ce programme s&amp;rsquo;est révélé productif au point de pouvoir répondre facilement à la question No 1 : oui, il y a des centaines de faux contre-exemples de la qualité de ceux des Simpson pour chaque p et 100 &amp;lt; a,b &amp;lt; 100'000.&lt;/p&gt;
&lt;p&gt;Le plus petit faux contre-exemple trouvé donnant une différence inférieure à 10-9 est &lt;strong&gt;1295³ + 216³ = 1297³&lt;/strong&gt; . La différence entre les deux termes 2181825071 et 2181825073 n&amp;rsquo;est que de 2 !&lt;/p&gt;
&lt;p&gt;Celui ayant la précision relative la plus élevée est  &lt;strong&gt;487674 + 245764 = 495354&lt;/strong&gt;  L&amp;rsquo;erreur relative n&amp;rsquo;est que de 5.1 10-16 soit juste deux fois plus que la précision des nombres flottants sur 64 bits de la plupart des calculatrices actuelles.&lt;/p&gt;
&lt;p&gt;A la puissance 5, le record de précision est 843855  + 834525 = 964035 (à 5.67 10-15 près), ensuite la précision des contre-exemples les moins faux diminue lentement quand la puissance augmente. A la puissance 12 il y a 1931112 + 1882112 = 2021812 (1.95 10-12) qui est meilleur que celui de David X. Cohen, qui s&amp;rsquo;est probablement limité aux nombres de 4 chiffres. Mais je n&amp;rsquo;ai rien remarqué de particulier pour n=12&lt;/p&gt;
&lt;p&gt;Voilà Andrew, comme 99.99% de la population je n&amp;rsquo;ai rien compris à ta prodigieuse démonstration
qui a nécessité 20 ans de vérifications avant que tu reçoive enfin ce prix bien mérité. Je me joins à David et Homer pour te dire juste Bravo !&lt;/p&gt;
&lt;h3 id="notes"&gt;Notes:&lt;/h3&gt;
&lt;p&gt;* la dernière ligne du tableau concerne évidemment la topologie (et non, un donut n&amp;rsquo;est pas isomorphe à une sphère), mais quelqu&amp;rsquo;un sait-il à quoi correspondent les lignes 1 et 3 ? Je doute que Cohen ait sorti ces équations de nulle part&amp;hellip;&lt;/p&gt;
&lt;p&gt;** pourquoi Python ? entre autres parce qu&amp;rsquo;il gère l&amp;rsquo;
de façon transparente : rien de spécial à faire pour manipuler des nombres énormes.&lt;/p&gt;
&lt;h3 id="références"&gt;Références&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span id="ref-1"&gt;&lt;/span&gt;&lt;span class="altmetric-embed" data-badge-type="donut" data-badge-popover="right" data-doi="10.2307/2118559" style="float: right; margin: 0 0 0.5rem 0.5rem;"&gt;&lt;/span&gt;
 Andrew Wiles. &amp;ldquo;Modular elliptic curves and Fermat&amp;rsquo;s Last Theorem&amp;rdquo;, 1995, Annals of Mathematics 142 (3): 443–551. 
. (
)&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-2"&gt;&lt;/span&gt;séquence &amp;ldquo;Homer³&amp;rdquo; dans &amp;ldquo;
&amp;quot;, épisode 6 saison 7, 1995&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-3"&gt;&lt;/span&gt;
, Les Simpson épisode 2 saison 10, 1998&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-4"&gt;&lt;/span&gt;video &amp;ldquo;
&amp;rdquo; de Numberphile sur ce sujet (en anglais)&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Einsum</title><link>https://drgoulu.com/2016/01/17/einsum/</link><pubDate>Sun, 17 Jan 2016 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2016/01/17/einsum/</guid><description>&lt;figure class="alignright" style="max-width: 300px; width: 100%;"&gt;&lt;img src="https://drgoulu.com/posts/2016/images/Einstein_laughing_hu_7e5e1e6b405da4d1.webp" srcset="https://drgoulu.com/posts/2016/images/Einstein_laughing_hu_7e5e1e6b405da4d1.webp 320w, https://drgoulu.com/posts/2016/images/Einstein_laughing_hu_bcdccbb8f7be008a.webp 480w, https://drgoulu.com/posts/2016/images/Einstein_laughing_hu_d485831c8e912c9f.webp 640w" sizes="(max-width: 640px) 100vw, 300px" alt="Sacré Albert, tu m&amp;#39;as scié encore une fois !" loading="lazy" class="rounded-lg shadow-sm w-full h-auto" data-zoomable /&gt;&lt;figcaption&gt;Sacré Albert, tu m&amp;rsquo;as scié encore une fois !&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;L&amp;rsquo;autre jour, j&amp;rsquo;ai rencontré Einstein se promenant incognito dans un bout de code.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai commencé par ne rien comprendre à une ligne de Python trouvée dans un algo de traitement d&amp;rsquo;image:&lt;/p&gt;
&lt;p&gt;norms = np.einsum(&amp;lsquo;ij,ij-&amp;gt;i&amp;rsquo;, X, X)&lt;/p&gt;
&lt;p&gt;Alors je suis allé voir la doc de la
 à laquelle je n&amp;rsquo;ai rien compris non plus, sauf que le bout du nez d&amp;rsquo;Albert commençait à apparaître via une mystérieuse &amp;ldquo;
&amp;rdquo; qui serait &amp;ldquo;un raccourci de notation utile pour la manipulation des équations concernant des coordonnées&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Par exemple, une simple
de deux matrices A et B s&amp;rsquo;écrit  &lt;/p&gt;
\[mathjax\]&lt;p&gt; &lt;/p&gt;
$$A^i{}_j \times B^j{}_k = C^i{}_k$$&lt;p&gt; avec la notation d&amp;rsquo;Einstein, et C=np.einsum(&amp;lsquo;ij,jk-&amp;gt;ik&amp;rsquo;, A, B) avec la fonction Python correspondante.&lt;/p&gt;
&lt;p&gt;On voit que la chaîne de caractères &amp;lsquo;ij,jk-&amp;gt;ik&amp;rsquo; correspond aux indices des lignes et colonnes des matrices qui figurent en indice et &amp;ldquo;exposant&amp;rdquo; dans la notation d&amp;rsquo;Einstein, dans le même ordre que dans la représentation de la multiplication matricielle ci-dessous:&lt;/p&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt=""
srcset="https://drgoulu.com/posts/2016/images/matrix_mul_reduce_hu_41d4b3edd58b6b83.webp 320w, https://drgoulu.com/posts/2016/images/matrix_mul_reduce_hu_885ab2a394beb458.webp 480w, https://drgoulu.com/posts/2016/images/matrix_mul_reduce_hu_8b9723dbd400fc90.webp 532w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://drgoulu.com/posts/2016/images/matrix_mul_reduce_hu_41d4b3edd58b6b83.webp"
width="532"
height="133"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Pour une simple multiplication matricielle on ne voit pas trop l&amp;rsquo;avantage sur la notation &lt;/p&gt;
$$A \times B = C$$&lt;p&gt; , mais ajoutons les 3 règles supplémentaires de la notation d&amp;rsquo;Einstein appliqués à la fonction einsum (traduit de
)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Les indices qui se répètent entre
(qui peuvent avoir N dimensions) signifient que les valeurs selon ces axes doivent être multipliées. Les produits font les valeurs du tableau de sortie. Dans le cas ci-dessus on a utilisé l&amp;rsquo;indice &amp;lsquo;j&amp;rsquo; une fois pour A et une fois pour B, ce qui signifie qu&amp;rsquo;on multiplie chaque ligne de A avec chaque colonne de B&lt;/li&gt;
&lt;li&gt;Une lettre omise dans le résultat signifie que les valeurs le long de cette axe doivent être sommées. Dans le produit matriciel ci-dessus, &amp;lsquo;j&amp;rsquo; ne figure pas dans les indices du tableau de sortie. Cette omission somme le long de cette &amp;ldquo;troisième direction en réduisant de 1 la dimension du tableau final. Si la signature avait été &amp;lsquo;ij,jk-&amp;gt;ijk&amp;rsquo;, on aurait obtenu une matrice 3x3x3 des produits, et si on n&amp;rsquo;avait écrit aucun indice mais gardé la flèche &amp;lsquo;ij,jk-&amp;gt;&amp;rsquo;, on aurait obtenu la somme de tous les éléments de C&lt;/li&gt;
&lt;li&gt;On peut obtenir les axes non sommés dans n&amp;rsquo;importe quel ordre. Si on omet la flèche, einsum arrange les indices qui n&amp;rsquo;apparaissent qu&amp;rsquo;une fois dans l&amp;rsquo;ordre alphabétique, donc on aurait pu écrire juste &amp;lsquo;ij,jk&amp;rsquo; au lieu de &amp;lsquo;ij,jk-&amp;gt;ik&amp;rsquo; pour le produit de matrices &amp;ldquo;normal&amp;rdquo; ci-dessus. Mais on aurait pu écrire &amp;lsquo;ij,jk-&amp;gt;ki&amp;rsquo; si on avait souhaité obtenir la
du résultat.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Avec ces astuces, beaucoup (toutes?) les opérations vectorielles, matricielles et sur des tableaux à N dimensions peuvent être effectuées par einsum&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signature de einsum&lt;/th&gt;
&lt;th&gt;équivalent NumPy&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;renvoie le vecteur A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('i-&amp;gt;', A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sum(A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;somme des valeurs du vecteur A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('i,i-&amp;gt;i', A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A * B&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;multiplication des vecteurs A et B élément par élément&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('i,i', A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;inner(A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
de A et B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('i,j', A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;outer(A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
de A et B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ij', A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;renvoie la matrice A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ji', A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A.T&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;transposée de A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ii-&amp;gt;i', A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;diag(A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;diagonale de A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ii', A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;trace(A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;somme de la diagonale de A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ij-&amp;gt;', A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sum(A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;somme des valeurs de A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ij-&amp;gt;j', A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sum(A, axis=0)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;somme des colonnes de A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ij-&amp;gt;i', A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sum(A, axis=1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;somme des lignes de A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ij,ij-&amp;gt;ij', A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A * B&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;multiplication des matrices A et B élément par élément&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ij,ji-&amp;gt;ij', A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A * B.T&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;multiplication des matrices A et B transposée élément par élément&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ij,jk', A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dot(A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
de A et B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ij,jk-&amp;gt;ij', A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;inner(A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;produit intérieur de A et B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ij,jk-&amp;gt;ijk', A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A[:, None] * B&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;chaque ligne de A multipliée par B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;('ij,kl-&amp;gt;ijkl', A, B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A[:, :, None, None] * B&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;chaque valeur de A multipliée par B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Les mathématiciens apprécieront l&amp;rsquo;élégance de la notation, les physiciens pourront manipuler le
et d&amp;rsquo;autres objets mathématiques avec plus de facilité, mais en quoi la fonction einsum est-elle utile aux informaticiens ?&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord, elle permet d&amp;rsquo;effectuer en une seule fois des opérations qui auraient nécessité de stocker des matrices intermédiaires sans ça. De plus le code de la fonction einsum peut être fortement
 puisqu&amp;rsquo;il effectue essentiellement des produits puis des sommes sur des vecteurs. Ce n&amp;rsquo;est en définitive que l&amp;rsquo;indexation des tableaux qui change en fonction de l&amp;rsquo;opération désirée, et le tout peut être optimisé dans certains cas en jouant avec la
et la commutativité des opérations. Grâce à ceci, la plupart des opérations listées dans le tableau ci-dessus sont entre 1 et 4 fois plus rapides avec einsum qu&amp;rsquo;avec leur équivalent Numpy, jamais plus lentes. Et pour des opérations plus complexes comme le calcul de la
, l&amp;rsquo;accélération peut être d&amp;rsquo;un facteur 10 ou plus
.&lt;/p&gt;
&lt;p&gt;Si vous n&amp;rsquo;avez pas tout suivi, retenez juste ceci:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Einstein n&amp;rsquo;était
du tout. Il était un surdoué en maths qui a préféré faire de la physique, mais le fait que sa &amp;ldquo;convention de sommation&amp;rdquo; apparaisse dans les langages de programmation un siècle plus tard confirme le génie du bonhomme&lt;/li&gt;
&lt;li&gt;Que Python est LE langage de programmation scientifique qui s&amp;rsquo;impose. Oubliez Matlab, qui n&amp;rsquo;offre même pas de fonction einsum &amp;hellip;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="références"&gt;Références&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span id="ref-1"&gt;&lt;/span&gt;&lt;span class="altmetric-embed" data-badge-type="donut" data-badge-popover="right" data-doi="10.1016/S0898-1221(02)00210-9" style="float: right; margin: 0 0 0.5rem 0.5rem;"&gt;&lt;/span&gt;
Åhlander, K. (2002). &amp;ldquo;Einstein summation for multidimensional arrays&amp;rdquo;, Computers and Mathematics with Applications, 44, 1007–1017.  
 
\[[pdf](http://www.nik.no/2000/Krister.Aahlander.pdf)\]&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-2"&gt;&lt;/span&gt;Alex Riley, &amp;ldquo;
&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-3"&gt;&lt;/span&gt;
sur MathWorld&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-4"&gt;&lt;/span&gt;
sur GitHub&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-5"&gt;&lt;/span&gt;
(pas simple&amp;hellip;)&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Comment dire 33 avec 3 cubes ?</title><link>https://drgoulu.com/2012/06/20/comment-dire-33-avec-3-cubes/</link><pubDate>Wed, 20 Jun 2012 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2012/06/20/comment-dire-33-avec-3-cubes/</guid><description>&lt;p&gt;Le gars qui m&amp;rsquo;a pourri ma dernière soirée du printemps s&amp;rsquo;appelle Mike Croucher.
 il a négligemment posé le problème suivant:&lt;/p&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;Il est possible d&amp;rsquo;écrire beaucoup d&amp;rsquo;entiers comme la somme des cubes de 3 entiers, par exemple: 99 = (-5)&lt;sup&gt;3 + 2&lt;/sup&gt;3+ 6^3 Un exemple plus compliqué est: 91 = (-67134)&lt;sup&gt;3 + (-65453)&lt;sup&gt;3+(83538)&lt;sup&gt;3 Votre tâche est de trouver les entiers x,y, et z tels que: 33 = x&lt;/sup&gt;3 + y&lt;/sup&gt;3 + z&lt;/sup&gt;3&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Comme j&amp;rsquo;ai bien vu que la solution pour 91 impliquait de grands entiers, je me suis dit que 33 demanderait probablement des nombres encore plus grands, mais j&amp;rsquo;ai quand même vite pondu le programme Python suivant pour voir:&lt;/p&gt;
\[sourcecode lang="Python"\]&lt;p&gt; res={} #dictionary of results def store(n,x,y,z): if n&amp;gt;=0 and n&amp;lt;100 and n not in res: res&lt;/p&gt;
\[n\]&lt;p&gt;=(x,y,z) print n,res&lt;/p&gt;
\[n\]&lt;p&gt;cube=[] x=0 while True: x3=x*x*x cube.append(x3) for y in range(x+1): y3=cube&lt;/p&gt;
\[y\]&lt;p&gt; for z in range(x+1): z3=cube&lt;/p&gt;
\[z\]&lt;p&gt; store(x3+y3+z3,x,y,z) store(x3-y3+z3,x,-y,z) store(x3+y3-z3,x,y,-z) store(x3-y3-z3,x,-y,-z) store(-x3+y3+z3,-x,y,z) store(-x3-y3+z3,-x,-y,z) store(-x3+y3-z3,-x,y,-z) # store(-x3-y3-z3,-x,-y,-z) #you&amp;rsquo;re right Neamar, it&amp;rsquo;s useless x+=1 if x==50: #show &amp;ldquo;small&amp;rdquo; results for n in range(100): if n in res: print n,res&lt;/p&gt;
\[n\]&lt;p&gt; else: print n,&amp;rsquo;?&amp;rsquo; &lt;/p&gt;
\[/sourcecode\]&lt;p&gt;En moins d&amp;rsquo;une seconde il trouve les 69 des 100 premiers entiers qui sont des sommes de cubes d&amp;rsquo;entiers inférieurs à 100. Le plus &amp;ldquo;compliqué&amp;rdquo; est 78=-55³+53³+26³ .&lt;/p&gt;
&lt;p&gt;Ensuite il mouline pendant des heures pour trouver 51=-796³+659³+602³ et 16=1626³-1609³-511³, et pendant ce temps j&amp;rsquo;ai exploré les liens émaillant les premiers commentaires 
.&lt;/p&gt;
&lt;p&gt;Il s&amp;rsquo;avère que le problème soumis par Mike Croucher est extrêmement difficile, voire impossible.
liste les solutions trouvées par mon programme, et d&amp;rsquo;autres qui ont nécessité des algorithmes beaucoup plus fûtés que le mien pour résoudre cette 
 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;87 = 4271³ - 4126³ - 1972³ , découvert en 1964
&lt;/li&gt;
&lt;li&gt;96 = -15250³ + 13139³ + 10853³&lt;/li&gt;
&lt;li&gt;91 = 83538³ - 67134³ - 65453³&lt;/li&gt;
&lt;li&gt;80 = -112969³ + 103532³ + 69241³&lt;/li&gt;
&lt;li&gt;39 = -159380³ + 134476³ + 117367³ découvert en 1991
&lt;/li&gt;
&lt;li&gt;75 = - 435203231³ + 435203083³ + 4381159³  découvert en 1993 
&lt;/li&gt;
&lt;li&gt;84 = 41639611³ - 41531726³ - 8241191³ découvert en 1994
&lt;/li&gt;
&lt;li&gt;30 =  2220422932³ - 2218888517³ - 283059965³ ainsi que 52 = -61922712865³ + 60702901317³ + 60702901317³ par la même équipe, qui a mis au point l&amp;rsquo;algo le plus performant du moment 
&lt;/li&gt;
&lt;li&gt;24 =15584139827³ - 15550555555³ - 2901096694³ a été découvert avec l&amp;rsquo;algo précédent en 2001 par D. J. Bernstein, dont la page
est consacrée à ce problème et son historique&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A part ça, les matheux démontrent (essayez&amp;hellip;) que les nombres de la forme 9n+4 et 9n+5 ne peuvent pas être la somme de trois cubes, ce qui règle le cas de 4,5,13,14,22,23 etc.&lt;/p&gt;
&lt;figure class="aligncenter" style="max-width: 412px; width: 100%;"&gt;&lt;a href="http://www.geluck.com/" target="_blank" rel="noopener"&gt;&lt;img src="https://drgoulu.com/posts/2012/images/9b493f196263e591d63658b2997f5b5b_hu_ac819ab557304383.webp" srcset="https://drgoulu.com/posts/2012/images/9b493f196263e591d63658b2997f5b5b_hu_ac819ab557304383.webp 320w, https://drgoulu.com/posts/2012/images/9b493f196263e591d63658b2997f5b5b_hu_5216f8ac3ff226ad.webp 412w" sizes="(max-width: 640px) 100vw, 412px" alt="Bon... je n ai pas vraiment le droit d&amp;#39;utiliser ce dessin, mais il va tellement bien là que j espère que vous ne m en voudrez pas, cher Docteur G..." loading="lazy" class="rounded-lg shadow-sm w-full h-auto" data-zoomable /&gt;&lt;/a&gt;&lt;figcaption&gt;Bon&amp;hellip; je n ai pas vraiment le droit d&amp;rsquo;utiliser ce dessin, mais il va tellement bien là que j espère que vous ne m en voudrez pas, cher Docteur G&amp;hellip;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Reste 33 (et 42 et beaucoup d&amp;rsquo;autres), qui résistent effrontément aux informaticiens cherchant à les obtenir, ainsi qu&amp;rsquo;aux mathématiciens essayant de prouver que c&amp;rsquo;est peine perdue&amp;hellip;&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est énervant qu&amp;rsquo;un petit problème tout bête comme celui-ci puisse résister ainsi aux ordinateurs les plus puissants.  Et motivant de penser qu&amp;rsquo;une solution est peut-être à la portée d&amp;rsquo;amateurs éclairés. Des idées ?&lt;/p&gt;
&lt;h3 id="références"&gt;Références:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span id="ref-1"&gt;&lt;/span&gt;Gardiner, V. L., Lazarus, R. B., &amp;amp; Stein, P. R. &amp;ldquo;
. &amp;ldquo;, 1964, Mathematics of Computation, 18, 408-413&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-2"&gt;&lt;/span&gt;Heath-Brown, D. R., Lioen, W. M., &amp;amp; te Riele, H. J. J.&amp;quot;
&amp;rdquo;, 1993, Mathematics of Computation, 61, 235-244&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-3"&gt;&lt;/span&gt;Andrew Bremner, &amp;ldquo;
&amp;rdquo;, 1993&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-4"&gt;&lt;/span&gt;B. Conn and L. Vaserstein, &amp;ldquo;On sums of three integral cubes&amp;rdquo;, 1994, Contemp. Math. 166, 285-294.&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-5"&gt;&lt;/span&gt;Eric Pine, Kim Yarbrough, Wayne Tarrant and Michael Beck, Noam D. Elkies, &amp;ldquo;
&amp;rdquo;, 2000, ANTS IV&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-6"&gt;&lt;/span&gt;Michael Beck, Eric Pine, Wayne Tarrant, Kim Yarbrough Jensen &amp;ldquo;New integer representations as the sum of three cubes&amp;rdquo;, 2007, Math. Comp. 76, 1683-1690 (
)&lt;/li&gt;
&lt;li&gt;&lt;span id="ref-7"&gt;&lt;/span&gt;&lt;span class="openbook_template5"&gt;&lt;a href="http://openlibrary.org/authors/OL4281353A/Wacław_Sierpiński" title="Click to view author in Open Library" target="_blank" rel="noopener"&gt;Wacław Sierpiński&lt;/a&gt; (1988). &lt;a href="http://openlibrary.org/books/OL2722939M/Elementary_theory_of_numbers" title="Click to view title in Open Library" target="_blank" rel="noopener"&gt;Elementary theory of numbers&lt;/a&gt;. North-Holland.&lt;span class="Z3988" title="ctx_ver=Z39.88-2004&amp;amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;amp;rft.genre=book&amp;amp;rft.btitle=Elementary+theory+of+numbers&amp;amp;rft.isbn=0444866620&amp;amp;rft.au=Wac%C5%82aw+Sierpi%C5%84ski&amp;amp;rft.pub=North-Holland&amp;amp;rft.date=1988"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; (
)&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>&amp;quot;jeu de l'année&amp;quot; 2012 et autres : c'est fini.</title><link>https://drgoulu.com/2012/01/18/jeu-de-lannee-2012-et-autres-cest-fini/</link><pubDate>Wed, 18 Jan 2012 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2012/01/18/jeu-de-lannee-2012-et-autres-cest-fini/</guid><description>&lt;p&gt;&lt;em&gt;(mis à jour plusieurs foirs après correction de bugs et améliorations, cf commentaires&amp;hellip;)&lt;/em&gt; &lt;/p&gt;
&lt;p&gt;Je m’apprêtais à passer une soirée tranquille quand je suis tombé sur un tweet de
 disant: &amp;ldquo;Qui va me battre au &amp;ldquo;jeu de l&amp;rsquo;année&amp;rdquo; ? 
&amp;rdquo;.&lt;/p&gt;
&lt;figure class="alignright" style="max-width: 250px; width: 100%;"&gt;&lt;img src="https://drgoulu.com/posts/2012/images/09a817e0eb516e6d72357bbf47b564a3_hu_dc589c41448b27fa.webp" srcset="https://drgoulu.com/posts/2012/images/09a817e0eb516e6d72357bbf47b564a3_hu_dc589c41448b27fa.webp 320w, https://drgoulu.com/posts/2012/images/09a817e0eb516e6d72357bbf47b564a3_hu_20b2c74f7bd203f9.webp 480w, https://drgoulu.com/posts/2012/images/09a817e0eb516e6d72357bbf47b564a3_hu_aa032b88a0192bd6.webp 500w" sizes="(max-width: 640px) 100vw, 250px" alt="Python bouffant du matheux" loading="lazy" class="rounded-lg shadow-sm w-full h-auto" data-zoomable /&gt;&lt;figcaption&gt;Python bouffant du matheux&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Un seul click m&amp;rsquo;a torpillé non pas une, mais trois soirées et un certain nombre d&amp;rsquo;heures de réflexion la journée. Mais ma vengeance est terrible : ni ElJj ni aucun autre mathématicien ne passera plus jamais des heures à former les entiers de 1 à 100 en n&amp;rsquo;utilisant que les chiffres de l&amp;rsquo;année (2,0,1,2) une et une seule fois chacun, les opérations de base +,-,*,/,^ et quelques autres. Fi-ni. Et c&amp;rsquo;est rapé aussi pour toutes les années futures ou passées, parce que j&amp;rsquo;ai fait un programme qui résout ce problème une fois pour toute. Amis
, je vous fais ci-dessous économiser des milliers d&amp;rsquo;heures que vous allez pouvoir utiliser pour résoudre de
 (Pensez à moi si vous en résolvez un, merci&amp;hellip;)&lt;/p&gt;
&lt;p&gt;Pour être honnête, la première soirée a été grillée à péniblement trouver 50 formules, ce qui m&amp;rsquo;a à la fois décidé à faire autrement et inspiré beaucoup de respect pour ElJj et les autres fous qui sont parvenus à obtenir des scores de 4000 et quelques points à la main. Bravo !&lt;/p&gt;
&lt;p&gt;Pour 2012, mes solutions sont donc : &lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;\[1, '(((2\*0)-1)^2)', 0\] \[2, '(((2-0)-1)\*2)', 0\] \[3, '(((2-0)-1)+2)', 0\] \[4, '(((2-0)^1)^2)', 0\] \[5, '(((2-0)+1)+2)', 0\] \[6, '(((2-0)+1)\*2)', 0\] \[7, '((2^0)+((1+2)!))', 0\] \[8, '(2^(0+(1+2)))', 0\] \[9, '(((2-0)+1)^2)', 0\] \[10, '((((2+0!)!)-1)\*2)', 0\] \[11, '(((2+0!)!)+(1/.2))', 1\] \[12, '((((2-0)+1)!)\*2)', 0\] \[13, '(2+sqrt((0!+((1/.2)!))))', 1\] \[14, '((((2+0!)!)+1)\*2)', 0\] \[15, '(((2-0)+1)/.2)', 1\] \[16, '(((2+0!)+1)^2)', 0\] \[17, '(((((2+0!)!)-1)!!)+2)', 5\] \[18, '(((2+0!)!)\*(1+2))', 0\] \[19, '((2^0)+(((1+2)!)!!!))', 5\] \[20, '(((2^0)/.1)\*2)', 1\] \[21, '(((((2+0!)!)!!!)+1)+2)', 5\] \[22, '((((2+0!)+1)!)-2)', 0\] \[23, '((.2^(-(0!+1)))-2)', 1\] \[24.0, '((((2-0)^1)^2)!)', 0\] \[25, '((((2+0!)!)-1)^2)', 0\] \[26, '((((2+0!)+1)!)+2)', 0\] \[27, '((2+0!)^(1+2))', 0\] \[28, '(((2+0!)/.1)-2)', 1\] \[29, '(((2+0!)/\[.1\])+2)&amp;rsquo;, 30] \[30, '((((2-0)+1)!)/.2)', 1\] \[31, '(((2+0!)/.1)+(Gamma(2)))', 31\] \[32, '(2^(-(0!-((1+2)!))))', 0\] \[33, '((((2+0!)!)!!)-((1/.2)!!))', 11\] \[34, '(sqrt((2^(0!/.1)))+2)', 1\] \[35, '((((2+0!)!)+1)/.2)', 1\] \[36, '((((2-0)+1)!)^2)', 0\] \[37, '(0!+(((1+2)!)^2))', 50\] \[38, '(((((2+0!)!)!!!)+1)\*2)', 5\] \[39, '(((.2^(-0!))!)-(\[.1\]^(-2)))&amp;rsquo;, 31] \[40, '(((2-0)/.1)\*2)', 1\] \[41, '(0!+(2/(.1/2)))', 51\] \[42, '((((2+0!)!)!!)-((1+2)!))', 5\] \[43, '((((2+0!)!)!!)-(1/.2))', 6\] \[44, '(-((.2-(0!/\[.1\]))/.2))&amp;rsquo;, 32] \[45, '(((((2+0!)!)!!)-1)-2)', 5\] \[46, '(((((2-0)+1)!)!!)-2)', 5\] \[47, '(((((2+0!)!)!!)+1)-2)', 5\] \[48, '((((2+0!)+1)!)\*2)', 0\] \[49, '((((2+0!)!)+1)^2)', 0\] \[50, '((.2^(-(0!+1)))\*2)', 1\]&lt;/th&gt;
&lt;th&gt;\[51, '((.2+(0!/.1))/.2)', 3\] \[52, '(2+(0!/(.1\*.2)))', 2\] \[53, '((((2+0!)!)!!)+(1/.2))', 6\] \[54, '((((2+0!)!)!!!)\*(1+2))', 5\] \[55, '(sqrt((((.2^(-0!))!)+1))/.2)', 2\] \[56, '(((((2+0!)!)+1)!!!)\*2)', 5\] \[57, '((((2+0!)!)!!)+sqrt((\[.1\]&lt;sup&gt;(-2))))&amp;rsquo;, 35] \[58, '((((2+0!)!)/.1)-2)', 1\] \[59, '((((2+0!)!)/.1)-(Gamma(2)))', 31\] \[60, '(((((2+0!)!)-1)!)/2)', 0\] \[61, '((((2+0!)!)/.1)+(Gamma(2)))', 31\] \[62, '((((2+0!)!)/.1)+2)', 1\] \[63, '((((2+0!)!)!!)+((1/.2)!!))', 11\] \[64, '(2^((0+(1+2))!))', 0\] \[65, '(0!+(2^((1+2)!)))', 50\] \[66, '((((2+0!)!)!!)+(((1+2)!)!!!))', 10\] \[67, '((((.2^(-0!))!!)-\[.1\])/\[.2\])&amp;rsquo;, 66] \[68, '(20+(((1+2)!)!!))', 15\] \[69, '((((0!+2)!)!!)+21)', 65\] \[70, '(((((2+0!)!)!)\*.1)-2)', 1\] \[71, 'sqrt((((((2+0!)!)+1)!)+(Gamma(2))))', 30\] \[72, 'sqrt((((((2+0!)!)!)\*.1)^2))', 1\] \[73, '(((((2+0!)!)!)\*.1)+(Gamma(2)))', 31\] \[74, '(((((2+0!)!)!)\*.1)+2)', 1\] \[75, '(((((2+0!)!)-1)!!)/.2)', 6\] \[76, '(-((.2^(-0!))-(\[.1\]&lt;/sup&gt;(-2))))&amp;rsquo;, 31] \[77, '((((2^2)!!)!!!)-sqrt((0!/\[.1\])))&amp;rsquo;, 90] \[78, '((((-2)+(0!/.1))!!!)-2)', 6\] \[79, '(-(2-(0!/(\[.1\]&lt;sup&gt;2))))&amp;rsquo;, 30] \[80, '(-((.2-0!)/(.1^2)))', 2\] \[81, '((((.2^(-0!))!!!)-1)^2)', 6\] \[82, '(2+(((0!/.1)-2)!!!))', 6\] \[83, '(2+(0!/(\[.1\]&lt;sup&gt;2)))&amp;rsquo;, 30] \[84, '((((.2^(-0!))!!!)!!!)\*(.1+.2))', 13\] \[85, '(((((2+0!)!)!!!)-1)/.2)', 6\] \[86, '((.2^(-0!))+(\[.1\]&lt;/sup&gt;(-2)))&amp;rsquo;, 31] \[87, '(((2+0!)!)+(\[.1\]&lt;/sup&gt;(-2)))&amp;rsquo;, 30] \[88, '(-((((2+0!)!)!)\*(.1-\[.2\])))&amp;rsquo;, 31] \[89, '(-((.2-((sqrt((0!/\[.1\]))!)!!!))/.2))&amp;rsquo;, 37] \[90, '(((((2-0)+1)!)!!!)/.2)', 6\] \[91, '(((.2^(-0!))!!!)+(\[.1\]^(-2)))&amp;rsquo;, 36] \[92, '(2\*(((sqrt((0!/\[.1\]))!)!!)-2))&amp;rsquo;, 35] \[93, '93', 500\] \[94, '(-(((2+0!)!)-(.1^(-2))))', 1\] \[95, '(-((.2^(-0!))-(.1^(-2))))', 2\] \[96, '(-((.2-0!)\*((1/.2)!)))', 2\] \[97, '(-(2+(0!-(.1^(-2)))))', 1\] \[98, '(-(2-(0!/(.1^2))))', 1\] \[99, '(-((2^0)-(.1^(-2))))', 1\] \[100, '(((2^0)/.1)^2)', 1\]&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;C&amp;rsquo;est un peu &amp;ldquo;brut de décoffrage&amp;rdquo; et mérite quelques explications. Chaque triplet comporte le nombre à obtenir, la &amp;ldquo;meilleure&amp;rdquo; formule qui le donne, et le coût de cette formule en points calculés selon les règles décrites dans l&amp;rsquo;article d&amp;rsquo;El Jj. Les formules qui utilisent les chiffres 2,0,1,2 dans l&amp;rsquo;ordre et les opérations arithmétiques de base et la factorielle (saviez-vous que 0!=1 ?) ont le coût minimum de 0. L&amp;rsquo;utilisation du point décimal bien utile avec une division ne coute qu&amp;rsquo;un point. Les formules qui utilisent des opérateurs plus compliqués comme les
(notées !! et !!!) et la
 sont pénalisées de 5 points.&lt;/p&gt;
&lt;p&gt;La formule pour 77 est la plus coûteuse (90 points) car elle utilise les chiffres dans le désordre (50 points), le développements décimal périodique &lt;/p&gt;
\[.1\]&lt;p&gt; = 0.11111111&amp;hellip; = 1/9 qui vaut 30 points chacun, plus deux multifactorielles. A noter que l&amp;rsquo;on a eu besoin d&amp;rsquo;inverser l&amp;rsquo;ordre des chiffres que dans 4 cas seulement.&lt;/p&gt;
&lt;p&gt;Et enfin, les nombre pour lesquels on ne trouve pas de solution sont pénalisés de 500 points, ce qui n&amp;rsquo;est le cas pour 93, pour lesquel je suis quasi certain qu&amp;rsquo;il n&amp;rsquo;existe pas de solution avec les opérations autorisées. Mais seulement &amp;ldquo;quasi&amp;rdquo;, pour des raisons que j&amp;rsquo;explique plus loin.&lt;/p&gt;
&lt;p&gt;Le total des pénalités que j&amp;rsquo;obtiens est de 1637 points&lt;/p&gt;
&lt;p&gt;Enfin, les formules ne sont pas très jolies d&amp;rsquo;une part à cause d&amp;rsquo;une &amp;ldquo;surparenthésation&amp;rdquo; qui simplifie l&amp;rsquo;écriture du programme (pas besoin de gérer la
), et d&amp;rsquo;autre part car le choix de la &amp;ldquo;meilleure&amp;rdquo; formule lorsqu&amp;rsquo;on en trouve plusieurs de score égal se fait par la longueur de la chaîne, sans tenir compte de l&amp;rsquo;ordre d&amp;rsquo;insertion ou de la priorité des opérateurs. Donc (((2*0)-1)^2) peut paraître plus compliqué que (((2*0)-1)+2) pour obtenir 1, mais pour une machine, c&amp;rsquo;est le même prix.&lt;/p&gt;
&lt;p&gt;Comme il se fait tard je coupe/colle le programme ci-dessous, je le commenterai plus tard (comme d&amp;rsquo;hab&amp;hellip;)&lt;/p&gt;
\[code lang="python" collapse="true"\]&lt;p&gt; # -*- coding: utf-8 -*- &amp;quot;&amp;quot;&amp;quot; Created on Mon Jan 16 17:19:27 2012&lt;/p&gt;
&lt;p&gt;@author: Philippe Guglielmetti solves
&amp;quot;&amp;quot;&amp;quot;&lt;/p&gt;
&lt;p&gt;import sys&lt;/p&gt;
&lt;p&gt;from math import sqrt,pow from scipy import factorial,factorial2,factorialk from itertools import permutations&lt;/p&gt;
&lt;p&gt;class f: &amp;ldquo;&amp;ldquo;&amp;ldquo;a formula&amp;rdquo;&amp;rdquo;&amp;rdquo; def __init__(self, m,p=0,v=None,parenthesis=False): #print m,&amp;rsquo;=&amp;rsquo;, self.isdigit=isinstance(m, (int, long)) self.math=str(m) #convert to string in case it&amp;rsquo;s a digit self.points=p if v is None: v=eval(self.math) self.isint=isinstance(v, (int,long)) if isinstance(v, float): #try to convert to int self.isint=abs(v-round(v))&amp;lt;1e-9 if self.isint: v=int(round(v)) if isinstance(v, long): #we don&amp;rsquo;t want bignums, for performance if abs(v)&amp;lt;sys.float_info.max: v=float(v) self.isint=True self.value=v&lt;/p&gt;
&lt;p&gt;if parenthesis: self.math=&amp;rsquo;(&amp;rsquo;+self.math+&amp;rsquo;)&amp;rsquo; #print self.value&lt;/p&gt;
&lt;p&gt;def __cmp__(self,other): if self.value&amp;lt;other.value:return -1 if self.value&amp;gt;other.value:return +1 if self.points&amp;lt;other.points:return -1 if self.points&amp;gt;other.points:return +1 if len(self.math)&amp;lt;len(other.math):return -1 if len(self.math)&amp;gt;len(other.math):return +1 return 0&lt;/p&gt;
&lt;p&gt;def __repr__(self): return str(&lt;/p&gt;
\[self.value,self.math,self.points\]&lt;p&gt;);&lt;/p&gt;
&lt;p&gt;class results(dict): &amp;ldquo;&amp;ldquo;&amp;ldquo;dictionary of formulas indexed by their result. we keep only the simplest entry for each result&amp;rdquo;&amp;rdquo;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;def __init__(self): dict.__init__(self)&lt;/p&gt;
&lt;p&gt;def add(self, f): if f.value in self.keys() and f&amp;gt;self&lt;/p&gt;
\[f.value\]&lt;p&gt; : pass else: self&lt;/p&gt;
\[f.value\]&lt;p&gt;=f&lt;/p&gt;
&lt;p&gt;def merge(self,d): for i in d.itervalues(): self.add(i)&lt;/p&gt;
&lt;p&gt;def score(self): return sum(map(lambda x: x.points,self.itervalues()))&lt;/p&gt;
&lt;p&gt;def notfound(self,p=500): return &lt;/p&gt;
\[i for (i,v) in self.iteritems() if v.points&gt;=p\]&lt;p&gt;def refine(self,recurse=2): &amp;ldquo;&amp;ldquo;&amp;ldquo;add combinations of monadic operators&amp;rdquo;&amp;rdquo;&amp;rdquo; r=results() for a in self.itervalues(): if recurse&lt;mark&gt;0 and a.value&amp;lt;&amp;gt;0: r.add(f(&amp;rsquo;-&amp;rsquo;+a.math,a.points,-a.value,True)) if a.isint: if a.value&lt;/mark&gt;0: r.add(f(a.math+&amp;rsquo;!&amp;rsquo;,a.points,1)) # 0! == 1 is very useful&amp;hellip; if a.value&amp;gt;1: s=f(&amp;lsquo;sqrt(&amp;rsquo;+a.math+&amp;quot;)&amp;quot;,a.points,sqrt(a.value)) if s.isint: r.add(s) if a.value&amp;gt;1 and a.value&amp;lt;19: r.add(f(a.math+&amp;rsquo;!&amp;rsquo;,a.points,factorial(int(a.value),True),True)) r.add(f(&amp;lsquo;Gamma(&amp;rsquo;+a.math+&amp;rsquo;)&amp;rsquo;,a.points+30,factorial(int(a.value)-1,True),True)) if a.value&amp;gt;3 and a.value&amp;lt;29: r.add(f(a.math+&amp;rsquo;!!&amp;rsquo;,a.points+5,factorial2(int(a.value),True),True)) if a.value&amp;gt;4 and a.value&amp;lt;39: r.add(f(a.math+&amp;rsquo;!!!&amp;rsquo;,a.points+5,factorialk(int(a.value),3,True),True)) self.merge(r) if recurse&amp;gt;0: self.refine(recurse-1)&lt;/p&gt;
&lt;p&gt;class monadic(results): &amp;ldquo;&amp;ldquo;&amp;ldquo;combinations of monadic operators around a formula&amp;rdquo;&amp;rdquo;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;def __init__(self, a): results.__init__(self) self.add(a) if a.value!=0: if a.isdigit: self.add(f(&amp;rsquo;.&amp;rsquo;+a.math,a.points+1)) if a.value&amp;lt;10: self.add(f(&amp;rsquo;&lt;/p&gt;
\[.'+a.math+'\]&lt;p&gt;&amp;rsquo;,a.points+30,a.value/9.)) self.refine();&lt;/p&gt;
&lt;p&gt;class diadic(results): &amp;ldquo;&amp;ldquo;&amp;ldquo;combinations of diadic operators of 2 operands&amp;rdquo;&amp;rdquo;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;def __init__(self, a,b): results.__init__(self) for i in a.itervalues(): for j in b.itervalues(): if i.isdigit and j.isdigit: if i.value!=0: self.add(f(int(i.math+j.math),10)) self.add(f(i.math+&amp;rsquo;.&amp;rsquo;+j.math,11)) self.add(f(i.math+&amp;rsquo;+&amp;rsquo;+j.math,i.points+j.points,i.value+j.value,True)) self.add(f(i.math+&amp;rsquo;-&amp;rsquo;+j.math,i.points+j.points,i.value-j.value,True)) self.add(f(i.math+&amp;rsquo;*&amp;rsquo;+j.math,i.points+j.points,i.value*j.value,True)) if j.value!=0: self.add(f(i.math+&amp;rsquo;/&amp;rsquo;+j.math,i.points+j.points,float(i.value)/float(j.value),True)) try: #so many things can go wrong with next line&amp;hellip; self.add(f(i.math+&amp;rsquo;^&amp;rsquo;+j.math,i.points+j.points,pow(i.value,j.value),True)) except: pass if i.isint: try: #so many things can go wrong with next line&amp;hellip; self.add(f(&amp;lsquo;root(&amp;rsquo;+i.math+&amp;rsquo;,&amp;rsquo;+j.math+&amp;rsquo;)&amp;rsquo;,i.points+j.points+5,pow(j.value,1./i.value),True)) except: pass if i.isint and j.isint and i.value&amp;gt;=0 and j.value&amp;gt;0 and i.value/j.value&amp;lt;10: try: #so many things can go wrong with next line&amp;hellip; self.add(f(i.math+&amp;rsquo;!&amp;rsquo;+j.math,i.points+j.points+5,factorialk(i.value,j.value),True)) except: pass&lt;/p&gt;
&lt;p&gt;self.refine();&lt;/p&gt;
&lt;p&gt;print &amp;lsquo;building list of monadics&amp;rsquo; m=map(monadic,map(f,range(0,10))) #list of all monadics of digits&lt;/p&gt;
&lt;p&gt;#use @Memoized here to improve performance def d(a,b): return diadic(a,b)&lt;/p&gt;
&lt;p&gt;def year(n,display=True,verbose=True): solution=results() for i in range(1,101): solution.add(f(i,500))&lt;/p&gt;
&lt;p&gt;def addresults(a,p): if verbose : print &amp;lsquo;formulas&amp;rsquo;,len(a), improved=0 found=0 for (i,v) in a.items(): if isinstance(i, (int, long)) and i&amp;gt;0 and i&amp;lt;101: v.points+=p if v&amp;lt;solution&lt;/p&gt;
\[i\]&lt;p&gt;: if solution&lt;/p&gt;
\[i\]&lt;p&gt;.points==500: found+=1 else: improved+=1 solution&lt;/p&gt;
\[i\]&lt;p&gt;=v if verbose: print(&amp;lsquo;found :&amp;rsquo;+str(found)+&amp;rsquo; improved :&amp;rsquo;+str(improved)+&amp;rsquo; score:&amp;rsquo;+str(solution.score()))&lt;/p&gt;
&lt;p&gt;def combine (a,p): m0=m&lt;/p&gt;
\[a\[0\]&lt;p&gt;]; m1=m&lt;/p&gt;
\[a\[1\]&lt;p&gt;]; m2=m&lt;/p&gt;
\[a\[2\]&lt;p&gt;]; m3=m&lt;/p&gt;
\[a\[3\]&lt;p&gt;] if solution.notfound():addresults(d(d(d(m0,m1),m2),m3),p) if solution.notfound():addresults(d(d(m0,d(m1,m2)),m3),p) if solution.notfound():addresults(d(m0,d(m1,d(m2,m3))),p) if solution.notfound():addresults(d(m0,d(d(m1,m2),m3)),p) if solution.notfound():addresults(d(d(m0,m1),d(m2,m3)),p)&lt;/p&gt;
&lt;p&gt;y=&lt;/p&gt;
\[int(i) for i in str(n)\]&lt;p&gt; combine(y,0) y.sort() for i in permutations(y): combine(i,50)&lt;/p&gt;
&lt;p&gt;if display: for i in solution.itervalues():print i print n,solution.score(),solution.notfound()&lt;/p&gt;
&lt;p&gt;year(2012) &lt;/p&gt;
\[/code\]</description></item><item><title>les décorateurs, ou pourquoi j'aime toujours la programmation</title><link>https://drgoulu.com/2010/12/04/les-decorateurs-python/</link><pubDate>Sat, 04 Dec 2010 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2010/12/04/les-decorateurs-python/</guid><description>&lt;p&gt;Je programme des ordinateurs depuis 30 ans, et tous les 5 ans environ je me demande pourquoi je continue à aimer ça. Et à chaque fois je découvre quelque chose qui provoque un &amp;ldquo;éclair haha&amp;rdquo;, une de ces illuminations cérébrales où l&amp;rsquo;on entrevoit le Génie dans toute sa Pureté avant de retomber sur sa chaise motivé pour les 5 ans suivants.&lt;/p&gt;
&lt;p&gt;Le dernier a été provoqué cette semaine par les &amp;ldquo;décorateurs&amp;rdquo; de Python, dont je vous parlerai plus bas.&lt;/p&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt=""
srcset="https://drgoulu.com/posts/2010/images/1e6c2b5ad4fd77ee9c967945b326090c_hu_e001748075368d38.webp 320w, https://drgoulu.com/posts/2010/images/1e6c2b5ad4fd77ee9c967945b326090c_hu_a064c69313869d17.webp 350w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://drgoulu.com/posts/2010/images/1e6c2b5ad4fd77ee9c967945b326090c_hu_e001748075368d38.webp"
width="350"
height="311"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
Ma première extase informatique date donc de 1980 environ, je m&amp;rsquo;en souviens comme si c&amp;rsquo;était hier. Mon
2001 se programmait en BASIC, un langage qu&amp;rsquo;on a bien fait d&amp;rsquo;oublier. Mais une astuce (ou plutôt une horreur) sur le PET permettait de faire en sorte que le programme s&amp;rsquo;ajoute des lignes de programme à lui-même ! J&amp;rsquo;avais ainsi modifié le jeu &amp;ldquo;
&amp;rdquo; de
pour qu&amp;rsquo;il &amp;ldquo;apprenne&amp;rdquo; de nouveaux animaux de façon permanente. Un programme qui s&amp;rsquo;améliore lui-même, ça me fait encore rêver, 30 ans après&amp;hellip;&lt;/p&gt;
&lt;p&gt;Le second flash date de 1985, la grande époque de
. Un jour j&amp;rsquo;ai vu quelque chose comme ça:&lt;/p&gt;
\[pascal\]&lt;p&gt;function factorielle(n: integer): integer; begin if n &amp;lt;= 1 then result := 1 else result := n*factorielle(n-1); end;&lt;/p&gt;
\[/pascal\]&lt;p&gt;Une fonction qui s&amp;rsquo;appelle elle même ! Merveilleuse
, chaque fois que je te revois, je me rappelle mon émoi à notre première rencontre.&lt;/p&gt;
&lt;p&gt;Les années 1990 sont riches en &amp;ldquo;haha&amp;quot;s plus rapprochés mais moins intenses pendant mes études à l&amp;rsquo;EPFL : la
 en LISP, le moteur d&amp;rsquo;inférence de
,  la
 en d&amp;rsquo;ADA, qui semblait à l&amp;rsquo;époque bien plus importante que la programmation orientée objet, juste entrevue.  Et
, précurseur de la programmation parallèle trop tôt disparu.&lt;/p&gt;
&lt;p&gt;Un peu avant 1995, je découvre
et le flash se produit lorsque je comprends que c&amp;rsquo;est un véritable langage de programmation complet&amp;hellip; sans code ! Ou plus exactement que le langage graphique de LabView permet de programmer directement au niveau sémantique. Adieu &amp;ldquo;syntax error&amp;rdquo; ! Quel dommage que
vende cette merveille si cher et l&amp;rsquo;aie blindé de brevets défendus avec vigueur : un des concepts les plus innovants en programmation reste confiné à un cercle d&amp;rsquo;initiés alors qu&amp;rsquo;il aurait pu révolutionner l&amp;rsquo;informatique.&lt;/p&gt;
&lt;figure class="aligncenter" style="max-width: 821px; width: 100%;"&gt;&lt;img src="https://drgoulu.com/posts/2010/images/f364f53ba469dc47f8253d2c53b9d310_hu_686879f385f928bd.webp" srcset="https://drgoulu.com/posts/2010/images/f364f53ba469dc47f8253d2c53b9d310_hu_686879f385f928bd.webp 320w, https://drgoulu.com/posts/2010/images/f364f53ba469dc47f8253d2c53b9d310_hu_2452d5fa1fbd9acd.webp 480w, https://drgoulu.com/posts/2010/images/f364f53ba469dc47f8253d2c53b9d310_hu_379f97455f41ee57.webp 760w" sizes="(max-width: 640px) 100vw, 821px" alt="un programme LabView avec blocs concurrents, boucles, tests etc." loading="lazy" class="rounded-lg shadow-sm w-full h-auto" data-zoomable /&gt;&lt;figcaption&gt;un &amp;quot;programme&amp;quot; LabView avec blocs concurrents, boucles, tests etc.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Vers 2000, je constate qu&amp;rsquo;on m&amp;rsquo;avait menti : C++ est beaucoup plus qu&amp;rsquo;un C amélioré. C&amp;rsquo;est un grand festival de &amp;ldquo;haha&amp;rdquo; en quelques jours : les
, la
, l&amp;rsquo;
, la
, les
, le
, mais surtout la
(STL). Java et .NET ont des librairies de classes et fonctions à tout faire, mais C++ a une &amp;ldquo;méta-librairie&amp;rdquo;, un système qui produit du code très optimisé pour chaque opération spécifique, en utilisant notamment le concept d&amp;rsquo;
.&lt;/p&gt;
&lt;p&gt;En 2005, je lis &lt;span class="openbook_template5"&gt;&lt;a href="http://openlibrary.org/authors/OL2670915A/Andrei_Alexandrescu" title="Click to view author in Open Library" target="_blank" rel="noopener"&gt;Andrei Alexandrescu&lt;/a&gt; (2001). &lt;a href="http://openlibrary.org/books/OL22119374M/Modern_C_plus_plus_design" title="Click to view title in Open Library" target="_blank" rel="noopener"&gt;Modern C [plus plus] design: generic programming and design patterns applied&lt;/a&gt;. Addison-Wesley.&lt;span class="Z3988" title="ctx_ver=Z39.88-2004&amp;amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;amp;rft.genre=book&amp;amp;rft.btitle=Modern+C+%5Bplus+plus%5D+design%3A+generic+programming+and+design+patterns+applied&amp;amp;rft.isbn=0201704315&amp;amp;rft.au=Andrei+Alexandrescu&amp;amp;rft.pub=Addison-Wesley&amp;amp;rft.date=2001"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;. Flash d&amp;rsquo;illumination : c&amp;rsquo;est génial, mais j&amp;rsquo;ai pas tout compris. Je le relis: re-flash: c&amp;rsquo;est encore plus génial que je pensais. Je le lis une troisième fois (à part &amp;ldquo;Les Robots&amp;rdquo; d&amp;rsquo;Asimov je n&amp;rsquo;ai jamais lu un livre 3 fois) : ça y&amp;rsquo;est je suis converti au &amp;ldquo;
&amp;rdquo;, une approche révolutionnaire de la programmation autorisée par une exploitation transcendantale de la combinaison C++, STL, et #macros. Les C++istes trouveront un petit
qui les convaincra à la fois de la puissance de cette approche, et de sa lourdeur d&amp;rsquo;écriture&amp;hellip;&lt;/p&gt;
&lt;p&gt;Et nous voici donc en 2010 où, poursuivant
, je tombe sur ça:&lt;/p&gt;
\[python\]&lt;p&gt;@cache() def fib(n): if n &amp;lt; 2: return 1 return fib(n-1) + fib(n-2)&lt;/p&gt;
\[/python\]&lt;p&gt;Même ceux qui ne parlent pas Python reconnaîtront une fonction récursive calculant le n-ième terme de la suite de Fibonacci, mais il y a une astuce : le @cache().&lt;/p&gt;
&lt;p&gt;Ca s&amp;rsquo;appelle un &amp;ldquo;décorateur&amp;rdquo; et en l’occurrence, le @cache() ajoute un comportement de
 (déjà mentionné
) à la fonction : si on appelle la fonction fib pour un n déjà calculé précédemment, le @cache() va renvoyer le résultat mémorisé avec un gain de vitesse appréciable plutôt que de recalculer la fonction, et ceci sans rien changer à la fonction elle-même!&lt;/p&gt;
&lt;p&gt;Et bien sur, le décorateur est lui-même écrit en Python : c&amp;rsquo;est
, et c&amp;rsquo;est une version simplifiée d&amp;rsquo;un
avec la librairie Python. Une
très variés est en train de naître (voyez par exemple les
, chers collègues&amp;hellip;), mais il me semble que les décorateurs permettent surtout de réaliser des choses ressemblant beaucoup aux &amp;ldquo;politiques&amp;rdquo; d&amp;rsquo;Alexandrescu avec une syntaxe plus simple qu&amp;rsquo;en C++. (Pythonistes, voyez 
)&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est pour cela que j&amp;rsquo;aime toujours la programmation : les langages gagnent en pouvoir d&amp;rsquo;abstraction en même temps que moi, et vice-versa. Chaque fois que je programme dans un nouveau langage, tout est plus simple, plus clair et plus vite juste que dans le précédent, et, luxe suprême,  je dois presser moins de touches. Les ordinateurs sont restés des esclaves qui camouflent leur stupidité derrière une vitesse stupéfiante, mais les concepteurs de langages, eux sont devenus vraiment malins. Merci
, grâce à toi j&amp;rsquo;en reprends pour 5 ans.&lt;/p&gt;</description></item><item><title>Démosaïquification</title><link>https://drgoulu.com/2008/12/03/demosaiquification/</link><pubDate>Wed, 03 Dec 2008 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2008/12/03/demosaiquification/</guid><description>&lt;p&gt;
C&amp;rsquo;est très facile de faire une
comme celle ci-contre. Des sites comme
en quelques secondes des centaines d&amp;rsquo;images pour approximer une image de base.&lt;/p&gt;
&lt;p&gt;Par exemple, voici un détail du goulot du bécher ci-contre:&lt;/p&gt;
&lt;p&gt;
Mais combien de petites images distinctes sont utilisées pour produire la mosaïque ? Pour répondre à cette question, j&amp;rsquo;ai développé un petit programme avec
en utilisant la
&lt;/p&gt;
&lt;p&gt;Le programme produit un
, avec à côté toutes les copies de cette image identifiées par le programme.&lt;/p&gt;
&lt;p&gt;Le programme Python est ci-dessous, il est relativement simple, à l&amp;rsquo;exception de la fonction permettant de comparer les images pour laquelle j&amp;rsquo;ai pas mal ramé, et il reste un seuil numérique à ajuster au cas par cas, mais dans l&amp;rsquo;ensemble ça marche&amp;hellip;&lt;/p&gt;
&lt;p&gt;A quoi ça sert tout ça ? Outre à tester le traitement d&amp;rsquo;image en Python avec PIL, c&amp;rsquo;est pour gagner un concours, mais je ne vous dirai pas (encore) lequel
&lt;/p&gt;
\[code lang="python"\]&lt;p&gt;import Image; #PIL import ImageChops; import ImageStat; import math;&lt;/p&gt;
&lt;p&gt;im = Image.open(&amp;ldquo;mosaic.jpg&amp;rdquo;) smallx=40; smally=40;&lt;/p&gt;
&lt;p&gt;border=1; #ignore border of picture print im.size&lt;/p&gt;
\[0\]&lt;p&gt;,im.size&lt;/p&gt;
\[1\]&lt;p&gt; n=(im.size&lt;/p&gt;
\[0\]&lt;p&gt;/smallx)*(im.size&lt;/p&gt;
\[0\]&lt;p&gt;/smally) nx=math.sqrt(n)&lt;/p&gt;
&lt;p&gt;res=Image.new(im.mode,(smallx*nx,smally*n)) L=[]; jmax=0; y=0; while y&amp;lt;im.size&lt;/p&gt;
\[1\]&lt;p&gt;: x=0; while x&amp;lt;im.size&lt;/p&gt;
\[0\]&lt;p&gt;: box = (x+border, y+border, x+smallx-border, y+smally-border) mini = im.crop(box) mininb=ImageOps.equalize(mini.convert(&amp;lsquo;L&amp;rsquo;)) min=1000; for i in range(len(L)): rms=ImageStat.Stat( ImageChops.difference( mininb,L&lt;/p&gt;
\[i\]\[0\]&lt;p&gt; ) ).mean&lt;/p&gt;
\[0\]&lt;p&gt; if rms&amp;lt;min: f=i; min=rms; if min&amp;lt;30: #this must be tuned for each case &amp;hellip; j=L&lt;/p&gt;
\[f\]\[1\]&lt;p&gt; L&lt;/p&gt;
\[f\]\[1\]&lt;p&gt;=j+1 else: j=0 f=len(L) L.append(&lt;/p&gt;
\[mininb,1\]&lt;p&gt;) if j&amp;gt;jmax: jmax=j; box = (smallx*j+border, smally*f+border) res.paste(mini,box); x=x+smallx print &amp;lsquo;.&amp;rsquo;, y=y+smally&lt;/p&gt;
&lt;p&gt;res.crop((0,0,(jmax+1)*smallx,len(L)*smally)).save(&amp;lsquo;result.jpg&amp;rsquo;) print &amp;lsquo;DONE!&amp;rsquo; print len(L),&amp;lsquo;different images over&amp;rsquo;,n,&amp;lsquo;max&amp;rsquo;,jmax+1,&amp;lsquo;copies&amp;rsquo;&lt;/p&gt;
&lt;p&gt;res.crop((0,0,(jmax+1)*smallx,len(L)*smally)).save(&amp;lsquo;result.jpg&amp;rsquo;) print len(L),&amp;lsquo;different images over&amp;rsquo;,n,&amp;lsquo;max&amp;rsquo;,jmax+1,&amp;lsquo;copies&amp;rsquo;&lt;/p&gt;
\[/code\]</description></item><item><title>Python(x,y)</title><link>https://drgoulu.com/2008/10/17/pythonxy/</link><pubDate>Fri, 17 Oct 2008 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2008/10/17/pythonxy/</guid><description>&lt;p&gt;
 est un langage très puissant, mais livré dans un état un peu brut. Pour l&amp;rsquo;exploiter de façon efficace, il faut y ajouter des libraires graphiques, de calcul spécialisé ou d&amp;rsquo;accès à des bases de données par exemple. Certaines ne sont que des interfaces vers des librairies en C ou C++, ce qui implique souvent des étapes d&amp;rsquo;installation, voire de compilation. De plus on se heurte souvent à des conflits de version entre les librairies et Python lui-même, ce qui ne simplifie pas les choses. Enfin et surtout, un bon IDE (Environnement de Développement Intégré, à l&amp;rsquo;envers en anglais&amp;hellip;) est indispensable pour programmer au XXIème siècle.&lt;/p&gt;
&lt;p&gt;
 résout tout ces problèmes en proposant un installeur unique de la Parfaite Panoplie du Petit Programmeur Scientifique. Elle inclut:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
, évidemment (version 2.5.2 actuellement)&lt;/li&gt;
&lt;li&gt;
, le meilleur IDE de la Galaxie. Concu initialement pour Java, Eclipse permet de développer dans pratiquement tous les langages connus à l&amp;rsquo;aide de plugins.
est celui qui supporte Python.&lt;/li&gt;
&lt;li&gt;
, le &amp;ldquo;Minimalist GNU for Windows&amp;rdquo; qui comprend le célèbre compilateur C++ &amp;ldquo;GCC&amp;rdquo;. Grâce au plugin Eclipse
également fourni, Python(x,y) est donc en même temps un environnement de développement C++ très complet&lt;/li&gt;
&lt;li&gt;
pour exploiter
, le framework d&amp;rsquo;applications multiplateforme de Nokia. Intégré à Eclipse, l&amp;rsquo;ensemble permet de créer des GUI (Interfaces Utilisateur Graphiques, à l&amp;rsquo;envers en anglais)&lt;/li&gt;
&lt;li&gt;une myriade de librairies scientifiques pour le traitement du signal et d&amp;rsquo;images, le calcul symbolique, la visualisation graphique 2D et 3D, le calcul parallèle etc. La liste complète est
 et le tout est résumé dans ce beau &amp;ldquo;mind map&amp;rdquo; :&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt=""
srcset="https://drgoulu.com/posts/2008/images/6d05e9aa0deff401627a1d6e6ceeabae_hu_5b6b5253ed0380f0.webp 320w, https://drgoulu.com/posts/2008/images/6d05e9aa0deff401627a1d6e6ceeabae_hu_ee4eb05afd00dd83.webp 480w, https://drgoulu.com/posts/2008/images/6d05e9aa0deff401627a1d6e6ceeabae_hu_568877ee7f7eca28.webp 706w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://drgoulu.com/posts/2008/images/6d05e9aa0deff401627a1d6e6ceeabae_hu_5b6b5253ed0380f0.webp"
width="706"
height="468"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Un avantage non négligeable de Python(x,y) est qu&amp;rsquo;elle est une oeuvre de Pierre Raybaut. Je ne le connais pas, mais c&amp;rsquo;est un français, donc toute la documentation et le support de la distribution sont dans la langue de Molière.&lt;/p&gt;
&lt;p&gt;Que vous vouliez vous initier à Python, simplement utiliser des libriaires qui vous donneront accès à un équivalent de Matlab ou vous lancer dans le développement d&amp;rsquo;une grosse application Python, je ne peux que vous recommander chaudement d&amp;rsquo;installer Python(x,y)&lt;/p&gt;</description></item><item><title>Python : un petit Sudoku pour commencer</title><link>https://drgoulu.com/2008/10/12/python/</link><pubDate>Sun, 12 Oct 2008 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2008/10/12/python/</guid><description>&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt=""
srcset="https://drgoulu.com/posts/2008/images/c2281c83670e31d8aaab7cb642b824db_hu_8f3a76d4d0cffefc.webp 320w, https://drgoulu.com/posts/2008/images/c2281c83670e31d8aaab7cb642b824db_hu_ff85c168c6ba8746.webp 480w, https://drgoulu.com/posts/2008/images/c2281c83670e31d8aaab7cb642b824db_hu_230bd27ab1f5d3ef.webp 601w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://drgoulu.com/posts/2008/images/c2281c83670e31d8aaab7cb642b824db_hu_8f3a76d4d0cffefc.webp"
width="601"
height="203"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
Ces temps-ci, je découvre
, un langage de programmation très apprécié en particulier dans la communauté scientifique. Ce &amp;ldquo;
&amp;rdquo; intègre des concepts développés dans plusieurs langages récents, ce qui en fait peut-être le langage le plus complet disponible actuellement.&lt;/p&gt;
&lt;p&gt;Pour une première approche de ce langage, je vous propose l&amp;rsquo;analyse du&amp;hellip;&lt;/p&gt;
&lt;h3 id="plus-court-solveurde-sudoku"&gt;Plus Court Solveur de Sudoku&lt;/h3&gt;
&lt;p&gt;Voici un  programme en Python de 173 caractères seulement qui serait le plus court
connu actuellement :&lt;/p&gt;
\[sourcecode lang="python"\]&lt;p&gt;def r(a): i=a.find(&amp;lsquo;0&amp;rsquo;) if i&amp;lt;0:print a &lt;/p&gt;
\[m in\[(i-j)%9\*(i/9^j/9)\*(i/27^j/27|i%9/3^j%9/3)or a\[j\]&lt;p&gt;for j in range(81)]or r(a&lt;/p&gt;
\[:i\]&lt;p&gt;+m+a&lt;/p&gt;
\[i+1:\]&lt;p&gt;)for m in`14**7*9`]r(raw_input())&lt;/p&gt;
\[/sourcecode\]&lt;p&gt;Ce programme est extrêmement compact et condensé, voire cryptique à l&amp;rsquo;instar des
. Ce n&amp;rsquo;est pas forcément la meilleure façon de programmer, mais ça révèle souvent la puissance cachée de certains langages. Ce programme est décrit en anglais et en détail
, mais voici son principe en gros et en français:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;def r(a): ... r(raw_input())&lt;/code&gt; // définit la fonction &amp;ldquo;r&amp;rdquo; qui résout le sudoku, puis on l&amp;rsquo;appelle en passant en paramètre ce que l&amp;rsquo;utilisateur a entré au clavier. Ca doit être une chaine de 81 caractères contenant ligne par ligne les chiffres de 1 à 9 donnés, et des 0 aux emplacements vides.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;i=a.find('0') if i&amp;lt;0:print a&lt;/code&gt; // au début de la fonction, on cherche le premier emplacement vide. Si on ne le trouve pas, on imprime le sudoku résolu&lt;/p&gt;
&lt;p&gt;la partie principale de la fonction utilise deux concepts puissants de Python:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;la &amp;ldquo;lazy evaluation&amp;rdquo; qui fait que l&amp;rsquo;expression &amp;ldquo;a or b&amp;rdquo; est équivalente à &amp;ldquo;if not(a):b&amp;rdquo;&lt;/li&gt;
&lt;li&gt;la &amp;ldquo;compréhension de liste&amp;rdquo; qui permet de créer une liste en écrivant une boucle à l&amp;rsquo;intérieur de
\[crochets\]. Par exemple &amp;ldquo;l =
\[x\*\*2 for x in range(10)\]&amp;rdquo; crée la liste des carrés des 10 premiers nombres entiers&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;ainsi cette expression &lt;code&gt;[(i-j)%9*(i/9^j/9)*(i/27^j/27|i%9/3^j%9/3)or a[j]for j in range(81)]&lt;/code&gt; fournit la &amp;ldquo;liste d&amp;rsquo;exclusion&amp;rdquo; contenant les chiffres qui ne peuvent pas figurer dans le trou à la position i. On l&amp;rsquo;obtient en parcourant toutes les celllules et en ajoutant à la liste la valeur des cases a&lt;/p&gt;
\[j\]&lt;p&gt; situées sur la même ligne, la même colonne ou dans le même bloc que i. Le grand test compliqué détermine ces conditions en utilisant les opérateurs modulo (%), ou binaire (|) et ou exclusif (^).&lt;/p&gt;
&lt;p&gt;Finalement, le &lt;code&gt;[m in [...] or r(a[:i]+m+a[i+1:])for m in`14**7*9`]&lt;/code&gt; remplit successivement la grille avec les chiffres possibles en utilisant la &amp;rsquo;lazy evaluation&amp;rsquo; une fois de plus : ce n&amp;rsquo;est que si le chiffre m n&amp;rsquo;est pas dans la liste d&amp;rsquo;exclusion qu&amp;rsquo;on execute la partie droite du or, laquelle appelle récursivement la fonction r en lui passant la grille en paramètre la grille &lt;code&gt;a[:i]+m+a[i+1:]&lt;/code&gt;. Ici , l&amp;rsquo;opérateur + sert à concaténer des listes : d&amp;rsquo;abord les i-èmes premières cases, puis le chiffre m proposé pour la case vide, puis les cases à partir de la i+1 ème.&lt;/p&gt;
&lt;p&gt;Ce programme utilise donc une approche &amp;ldquo;force brute&amp;rdquo; loin d&amp;rsquo;être optimale en temps de calcul : on essaie les chiffres possibles dans chaque case vide jusqu&amp;rsquo;à ce que ça marche.&lt;/p&gt;
&lt;p&gt;Reste à découvrir une astuce de geek que je ne connaissais pas : 14**7*9 vaut 948721536, un nombre qui contient les chiffres 123456789, dans le désordre, mais ça va aussi pour notre application. Les backquotes ´&amp;hellip;´ servent à former une chaine, comme d&amp;rsquo;ailleurs la fonction str(). L&amp;rsquo;avantage de ´14**7*9´sur ´123456789´ ? ben c&amp;rsquo;est évident : il faut deux bytes de moins &amp;hellip;&lt;/p&gt;</description></item><item><title>Palindrome de 196</title><link>https://drgoulu.com/2008/09/14/palindrome-de-196/</link><pubDate>Sun, 14 Sep 2008 00:00:00 +0000</pubDate><guid>https://drgoulu.com/2008/09/14/palindrome-de-196/</guid><description>&lt;figure class="alignright" style="max-width: 240px; width: 100%;"&gt;&lt;a href="http://www.flickr.com/photos/puja/251008157/" target="_blank" rel="noopener"&gt;&lt;img src="https://drgoulu.com/posts/2008/images/ba5485f7dc2d3ad49c29a5e809e73964_hu_a9217b2d24292c9e.webp" srcset="https://drgoulu.com/posts/2008/images/ba5485f7dc2d3ad49c29a5e809e73964_hu_a9217b2d24292c9e.webp 240w" sizes="(max-width: 640px) 100vw, 240px" alt="palindrome par puja (flickr)" loading="lazy" class="rounded-lg shadow-sm w-full h-auto" data-zoomable /&gt;&lt;/a&gt;&lt;figcaption&gt;palindrome par puja (flickr)&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Prenons un nombre au hasard : 1729. Ecrivons-le à l&amp;rsquo;envers : 9271 et additionnons les deux nombres : 1729+9271=11000. Recommençons avec ce nombre : 11000+00011 = 11011.&lt;/p&gt;
&lt;p&gt;Ce nombre est égal à lui même écrit à l&amp;rsquo;envers, c&amp;rsquo;est un nombre
, comme le mot &amp;ldquo;radar&amp;rdquo; ou la phrase &amp;ldquo;élu par cette crapule&amp;rdquo; si on ne tient pas compte des espaces, ou encore &amp;ldquo;
&amp;rdquo;, un texte de 5566 lettres écrit par Georges Perec en 1969.&lt;/p&gt;
&lt;p&gt;Tiens, refaisons le calcul avec 1969 pour voir : 1969+9691 = 11660 11660+06611 = 18271 18271+17281 = 35552 35552+25553 = 61105 61105+50116 = 111221 111221+122111 = 23332 : palindrome !&lt;/p&gt;
&lt;p&gt;Bizarre n&amp;rsquo;est-ce pas ? En fait on peut prendre n&amp;rsquo;importe quel nombre de départ, et la séquence d&amp;rsquo;opérations arrive inévitablement à un nombre palindrome en un nombre fini d&amp;rsquo;itérations. C&amp;rsquo;est du moins ce que l&amp;rsquo;on
, car il y a deux problèmes :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;nos amis mathématiciens n&amp;rsquo;ont aucune idée de la raison qui fait que ça marche, entre autre parce que &amp;ldquo;écrire un nombre en base 10 à l&amp;rsquo;envers&amp;rdquo; n&amp;rsquo;a pas de signification mathématique simple.&lt;/li&gt;
&lt;li&gt;il y a un gros hic. Si on commence avec 196, on n&amp;rsquo;arrive pas à un palindrome même après 300 millions d&amp;rsquo;itérations&amp;hellip;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;196 est le plus petit des &amp;ldquo;
&amp;rdquo; (en l&amp;rsquo;honneur de Cheryl, la petite amie du matheux qui a proposé cette propriété) : les nombres qui n&amp;rsquo;aboutissent apparemment pas à un palindrome &amp;hellip;
se trouvent évidemment
, mais aussi quelques autres.&lt;/p&gt;
&lt;p&gt;Ce qui est intéressant aussi, c&amp;rsquo;est la distribution du nombre d&amp;rsquo;itérations : très souvent, on atteint le palindrome en quelques étapes, même pour de très grands nombres, mais quelques rares nombres demandent quelques étapes de plus&lt;/p&gt;
&lt;p&gt;Par exemple pour les nombres de 1 à 99, on arrive à un palindrome en 4 itérations ou moins  pour 98 nombres, 79 et 97 ont besoin de 6 itérations, mais 89 et 98 nécessitent 24 itérations.&lt;/p&gt;
&lt;p&gt;Pour les nombres de 1 à 999, 929 atteignent le palindrome en 6 itérations ou moins, 13 sont des nombres de Lychrel et les 57 restant ont besoin de moins de 24 étapes : 89 et 98 sont toujours les plus gourmands en itérations&lt;/p&gt;
&lt;p&gt;Poussons jusqu&amp;rsquo;à 10'000 : à part les 249 (&lt;em&gt;edit du 6.1.14 : et non 246&lt;/em&gt;) nombres de Lychrel, il n&amp;rsquo;y a toujours pas d&amp;rsquo;autre nombre que 89 et 98 qui nécessite plus de 24 itérations.&lt;/p&gt;
&lt;p&gt;La quête du &amp;ldquo;
&amp;rdquo; a commencé par ces résultats :&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Nombre&lt;/th&gt;
&lt;th&gt;Iterations&lt;/th&gt;
&lt;th&gt;Palindrome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10911 147996 150296 1000689 1005744 1017501 7008899 9008299&lt;/td&gt;
&lt;td&gt;55 58 64 78 79 80 82 96&lt;/td&gt;
&lt;td&gt;4668731596684224866951378664 8834453324841674761484233544388 682049569465550121055564965940286 796589884324966945646549669423488985697 796589884324966945646549669423488985697 14674443960143265333356234106934447641 68586378655656964999946965655687368586 555458774083726674580862268085476627380477854555&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;le record du monde actuel (2008) date de 2005 et concerne le nombre 1'186'060'307'891'929'990, qui produit un palindrome de 119 chiffres après 162 itérations.&lt;/p&gt;
&lt;p&gt;On voit que le nombre maximal d&amp;rsquo;itérations requises n&amp;rsquo;augmente que très lentement, ce qui rend les nombres de Lychrel encore plus intriguants et exceptionnels.&lt;/p&gt;
&lt;p&gt;A propos : j&amp;rsquo;ai commencé cet article par
est peu intéressant : 1548+8451 = 9999. Ou alors, 30 ans plus tard, je viens de découvrir une nouvelle propriété de ce nombre &amp;hellip;&lt;/p&gt;
&lt;h3 id="références"&gt;Références:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
pour le calcul de palindromes&lt;/li&gt;
&lt;li&gt;
 sur MathWorld&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>