<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Suboptimal</title>
    <link rel="self" type="application/atom+xml" href="https://suboptimal.fr/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://suboptimal.fr"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2023-09-17T00:00:00+00:00</updated>
    <id>https://suboptimal.fr/atom.xml</id>
    <entry xml:lang="en">
        <title>Réélections</title>
        <published>2023-09-17T00:00:00+00:00</published>
        <updated>2023-09-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://suboptimal.fr/2023-09-reelections/"/>
        <id>https://suboptimal.fr/2023-09-reelections/</id>
        
        <content type="html" xml:base="https://suboptimal.fr/2023-09-reelections/">&lt;p&gt;Je me remets à écrire sur le sujet des élections (et offre un dernier râle à ce blog moribond) à la faveur de la publication, il y a quelques jours, d&#x27;un livre de Julia Cagé et Thomas Piketty. Le Monde en a fait un article relativement dithyrambique &lt;a href=&quot;https:&#x2F;&#x2F;www.lemonde.fr&#x2F;idees&#x2F;article&#x2F;2023&#x2F;09&#x2F;05&#x2F;julia-cage-et-thomas-piketty-livrent-une-vision-inedite-de-l-histoire-politique-francaise_6187990_3232.html&quot;&gt;ici&lt;&#x2F;a&gt;. En complément du livre, Piketty et collègues&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#collegues&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; ont récolté et publié &lt;a href=&quot;https:&#x2F;&#x2F;unehistoireduconflitpolitique.fr&#x2F;telecharger.html&quot;&gt;un ensemble de données&lt;&#x2F;a&gt; fascinant : les résultats des élections présidentielles et législatives par communes de 1789 à 2022&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#metro&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Évidemment, ça me donne l&#x27;occasion de revenir sur l&#x27;analyse du &lt;a href=&quot;https:&#x2F;&#x2F;suboptimal.fr&#x2F;2022-04-elections&#x2F;&quot;&gt;billet précédent&lt;&#x2F;a&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#mieux&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Pour rappel, l&#x27;idée était d&#x27;utiliser le détail du vote des communes pour estimer les flux d&#x27;électeurs (ou reports de voix). Le flux d&#x27;électeur de X à Y c&#x27;est la proportion de Français qui, ayant voté pour X à une élection, votent pour Y à l&#x27;élection suivante. Je suis aussi (évidemment) retombé dans le puits sans fond des statistiques associées. Donc ce billet va être composé de 10 % de jolis graphes, de 10 % de socio de café du commerce et de 80 % de stats. En gros la même chose que le billet précédent, mais avec plus de données et (j&#x27;espère) des stats un peu plus solides. Pour commencer par les trucs jolis (&lt;a href=&quot;https:&#x2F;&#x2F;suboptimal.fr&#x2F;2023-09-reelections&#x2F;sankey_vieme.html&quot;&gt;ici&lt;&#x2F;a&gt; pour le même en pleine page) :&lt;&#x2F;p&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;vieme_election.html&quot; height=&quot;500&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Les traits en gris qui apparaissent lorsque vous survolez le diagramme sont les flux qui ne sont pas significatifs statistiquement. Si vous voulez sauter les trucs chiants et aller voir des jolis graphes, c&#x27;est &lt;a href=&quot;https:&#x2F;&#x2F;suboptimal.fr&#x2F;2023-09-reelections&#x2F;#sociologie-de-comptoir&quot;&gt;ici&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;douleurs-statistiques&quot;&gt;Douleurs statistiques&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;If your experiment needs statistics, you ought to have done a better experiment.&lt;&#x2F;p&gt;
&lt;p&gt;-- &lt;cite&gt;Ernest Rutherford&lt;&#x2F;cite&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Donc, comment obtient-on un flux d&#x27;électeurs entre deux années électorales à partir des résultats de chaque commune ? Imaginons qu&#x27;une commune ait voté à 100 % pour Mitterrand en 1988, puis qu&#x27;en 1995 le vote se soit séparé et que 80 % des électeurs aient voté pour Jospin et 20 % pour Chirac. On en déduit donc que 4&#x2F;5 des électeurs de Mitterrand ont voté pour Jospin à l&#x27;élection suivante et que le reste a voté pour Chirac.&lt;&#x2F;p&gt;
&lt;p&gt;Cette situation hypothétique est évidemment complètement con :&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Pas beaucoup de communes votent à 100 % pour un candidat. On se retrouve alors avec plus d&#x27;inconnues (les flux) que de données (les résultats des élections). Pour s&#x27;en tirer, on doit combiner les résultats de plusieurs communes, en supposant qu&#x27;elles ont les mêmes flux.&lt;&#x2F;li&gt;
&lt;li&gt;Mais ça pose un vrai problème ; les communes ne sont pas toutes identiques et deux électeurs de Mitterrand ne se valent pas. Un bobo parisien et un CGTiste Roubaisien peuvent voter pour le même candidat une année donnée, mais ils représentent évidemment des classes sociales très différentes aux dynamiques de vote qui vont aussi l&#x27;être. En langage statistique, les communes ne sont pas identiquement distribuées.&lt;&#x2F;li&gt;
&lt;li&gt;Même deux communes parfaitement identiques sociologiquement vont avoir des résultats &#x2F; des flux différents. Les électeurs votent en partie de manière &amp;quot;aléatoire&amp;quot; : leur contexte géographique, socio-économique et leur historique de vote ne sont pas suffisants pour savoir pour qui ils vont voter (et heureusement).&lt;&#x2F;li&gt;
&lt;li&gt;5 ans, voire 7, entre deux élections présidentielles, c&#x27;est long (putain, 5 ans). Suffisamment long pour que des ados deviennent majeurs et commencent à voter et pour que des gens déménagent ou meurent, et donc arrêtent de voter (&lt;a href=&quot;https:&#x2F;&#x2F;fr.wikipedia.org&#x2F;wiki&#x2F;Jean_Tiberi#Affaire_des_faux_%C3%A9lecteurs_du_5e_arrondissement&quot;&gt;généralement&lt;&#x2F;a&gt;). Environ 7 % des Français déménagent dans une autre commune chaque année. Environ 1 % meurent. Une partie des flux va donc représenter des remplacements de population&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#eric&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; plutôt que des changements d&#x27;opinion.&lt;&#x2F;li&gt;
&lt;li&gt;Sans même commencer à parler des données manquantes ou erronées : pour beaucoup de communes, on n&#x27;a que des données partielles (Toulouse ou Grenoble) et un ensemble de communes appartenant à un département imaginaire (la &amp;quot;Meurthe-et-Moselle&amp;quot;) apparaissent presque tous les ans, sauf en 1988&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#plainte&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Avec toutes ces réserves, on peut raisonnablement se dire que toute l&#x27;entreprise est à abandonner. Mais bon, on a aussi beaucoup de données (30 000+ communes), alors allons-y quand même.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Malgré mon plus profond désir de faire court et clair, les parties qui suivent ont tout du &lt;em&gt;Methods&lt;&#x2F;em&gt; écrit par un graphomane payé à la lettre. Les stats en elles-même sont aussi usine-à-gaz-esque.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;quantita-tif&quot;&gt;Quantita &#x27;tif&lt;&#x2F;h3&gt;
&lt;p&gt;Tout n&#x27;est pas perdu, donc. On peut faire les choses bien. Faire des validations croisées, des corrections statistiques, des maths. Pour chaque commune $c$, on cherche une matrice de flux $F_{c, i, j}$ dont la taille dépend du nombre de candidats aux deux élections, et qui vérifie les deux contraintes assez évidentes :&lt;&#x2F;p&gt;
&lt;p&gt;$$
F_{c, i, j} \geq 0 \quad \text{et} \quad \sum_j F_{i, j, c} = 1
$$&lt;&#x2F;p&gt;
&lt;p&gt;Pour trouver cette matrice, on va essayer de minimiser l&#x27;expression :&lt;&#x2F;p&gt;
&lt;p&gt;$$
\sum_j \left( \sum_i R^1_{i, c} F_{c, i, j} - R^2_{j, c} \right)^2
$$&lt;&#x2F;p&gt;
&lt;p&gt;Où $R^k_{j, c}$ est la proportion de votants pour le candidat $j$ à l&#x27;élection $k$ dans la commune $c$. Comme on ne considère qu&#x27;une seule commune, il n&#x27;y a pas une solution unique. Si on veut un résultat un peu solide, il va donc falloir regrouper plusieurs communes et minimiser :&lt;&#x2F;p&gt;
&lt;p&gt;$$
\sum_c \sum_j \left( \sum_i R^1_{i, c} F_{c, i, j} - R^2_{j, c} \right)^2
$$&lt;&#x2F;p&gt;
&lt;p&gt;Soit. Mais quelles communes regroupe-t-on exactement ? Considérer la France entière est une très mauvaise idée : des communes différentes n&#x27;ont aucune raison d&#x27;avoir des matrices de flux identiques&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#poids&quot;&gt;6&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Pour corriger ça, on va vouloir prendre un ensemble de communes relativement similaires à celle qui nous intéresse. Mais des communes similaires vont aussi &lt;em&gt;voter&lt;&#x2F;em&gt; de manière similaire et donc apporter moins d&#x27;informations sur la valeur réelle de $F$. Par exemple, dans le cas extrême où les proportions des votes sont exactement identiques, avoir plus d&#x27;une commune n&#x27;apporte strictement rien. C&#x27;est une jolie illustration du &lt;a href=&quot;https:&#x2F;&#x2F;fr.wikipedia.org&#x2F;wiki&#x2F;Dilemme_biais-variance&quot;&gt;dilemme biais-variance&lt;&#x2F;a&gt;, on a le choix entre regrouper beaucoup de communes — augmenter le biais — et faire des regroupements plus petits, de communes très similaires — ce qui rend les valeurs de $F$ moins précises.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;villes-jumelles&quot;&gt;Villes jumelles&lt;&#x2F;h3&gt;
&lt;p&gt;Bon, c&#x27;est bien joli de parler de &amp;quot;communes similaires&amp;quot;, mais comment fait-on exactement ? Comme j&#x27;ai déjà passé suffisamment de temps sur ce projet, j&#x27;ai choisi une méthode assez bourrine. Au sein d&#x27;une même région (pré-2015, les nouvelles régions ont des noms à la con&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#pre2015&quot;&gt;7&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;), on considère l&#x27;ensemble des résultats électoraux et on utilise la &lt;a href=&quot;https:&#x2F;&#x2F;fr.wikipedia.org&#x2F;wiki&#x2F;M%C3%A9thode_des_k_plus_proches_voisins&quot;&gt;méthode des k plus proches voisins&lt;&#x2F;a&gt;. On obtient $K$ communes, qui sont les plus proches, électoralement parlant, de celle que l&#x27;on considère.&lt;&#x2F;p&gt;
&lt;p&gt;Pour valider les résultats et regarder à quel point on fait de la merde, on fait une validation croisée. Par exemple, on met de côté la commune originale dans l&#x27;algorithme et on regarde à quel point les valeurs de $F$ obtenues permettent de prédire le résultat de la seconde élection à partir de la première. Il y a un optimum à trouver entre précision et biais sur le nombre de voisins ; j&#x27;ai fini par choisir $K = 50$ qui avait l&#x27;air de ne pas être un trop mauvais compromis&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#stat&quot;&gt;8&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;De toute façon, ces prédictions sont en général de mauvaise qualité : même sur les résultats d&#x27;un gros candidat, on va avoir des erreurs qui peuvent aller jusqu&#x27;à cinq pour cent des voix. Ce n&#x27;est pas &lt;em&gt;si&lt;&#x2F;em&gt; affreux, on travaille avec un modèle avec assez peu de paramètres, et la corrélation est très proche de celle obtenue avec le meilleur modèle linéaire&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#linéaire&quot;&gt;9&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Cependant, ça met en lumière la limitation principale de ce type de modélisation. Les votes ne sont pas entièrement déterminés par le contexte social-politique-géographique-économique ; parfois, les gens prennent des décisions tout seuls comme des grands. Et parfois, ils votent au hasard. Beaucoup de petits candidats, surtout les &amp;quot;contestataires&amp;quot;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#vote_contestataire&quot;&gt;10&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, sont extrêmement aléatoires. La prédiction d&#x27;un modèle linéaire pour Le Pen en 1981 est complètement foireuse, par exemple.&lt;&#x2F;p&gt;
&lt;div style=&quot;display: flex;&quot;&gt;
  &lt;img src=&quot;&#x2F;img&#x2F;chirac_1981_1988.png&quot; alt=&quot;Prédiction du vote Le Pen en 1981&quot; style=&quot;width: 50%;&quot;&gt;
  &lt;img src=&quot;&#x2F;img&#x2F;lepen_1981_1988.png&quot; alt=&quot;Prédiction du vote Chirac en 1981&quot; style=&quot;width: 50%;&quot;&gt;
&lt;&#x2F;div&gt;
&lt;h3 id=&quot;groupir&quot;&gt;Groupir&lt;&#x2F;h3&gt;
&lt;p&gt;Maintenant qu&#x27;on a des estimations de flux pour chaque village de France et de Navarre, il reste à regrouper toutes ces estimations. C&#x27;est là que les questions comme &amp;quot;qu&#x27;est-ce qu&#x27;un flux significatif&amp;quot; vont entrer en compte. Intuitivement, on a juste envie de prendre la valeur du flux de chaque commune et de la multiplier par le nombre d&#x27;habitants de ladite commune et d&#x27;aller faire des choses plus intéressantes de ma vie. Mais c&#x27;est un peu bourrin de faire ça directement, parce que les flux ne peuvent pas être négatifs. Donc si l&#x27;on observe la distribution d&#x27;un flux très faible dans des communes proches, on va observer une distribution qui a un pic en 0 et du bruit autour (en fait, une &lt;a href=&quot;https:&#x2F;&#x2F;fr.wikipedia.org&#x2F;wiki&#x2F;Loi_tronqu%C3%A9e#Loi_normale_tronqu%C3%A9e&quot;&gt;loi normale tronquée&lt;&#x2F;a&gt;). La moyenne du flux est artificiellement poussée vers le haut par l&#x27;impossibilité de définir des flux négatifs. On peut corriger ça assez bien en calculant la moyenne de la loi normale sous-jacente plutôt que la moyenne observée.&lt;&#x2F;p&gt;
&lt;div style=&quot;display: flex;&quot;&gt;
  &lt;img src=&quot;&#x2F;img&#x2F;trunc_norm_chirac_mitterand.png&quot; alt=&quot;Distribution du flux Chirac -&gt; Mitterrand&quot; style=&quot;width: 50%;&quot;&gt;
  &lt;img src=&quot;&#x2F;img&#x2F;trunc_norm_chirac_giscard.png&quot; alt=&quot;Distribution du flux Chirac -&gt; Giscard&quot; style=&quot;width: 50%;&quot;&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;On peut aussi utiliser ces distributions pour estimer quels flux sont &amp;quot;statistiquement significatifs&amp;quot; et lesquels ne le sont pas. On s&#x27;enfonce solidement dans le maquis statistique ici, mais l&#x27;idée est de dire que si les flux varient beaucoup (en valeur relative) entre deux communes voisines, alors ils ne sont probablement pas représentatifs d&#x27;un réel changement d&#x27;opinion. L&#x27;algorithme aura tendance à &amp;quot;boucher les trous&amp;quot; lorsqu&#x27;il ne sait pas, et à inventer des flux fantômes. Par ailleurs, pour les plus petits candidats, on n&#x27;a tout simplement pas assez de données solides. J&#x27;élimine ces flux supplémentaires a posteriori (un peu arbitrairement, il faut bien dire) : si le coefficient de variation moyen est supérieur à une valeur arbitraire (0.75 ici), alors le flux est considéré comme non significatif.&lt;&#x2F;p&gt;
&lt;p&gt;Un dernier point, à la fin de l&#x27;analyse, rien n&#x27;impose que la somme des votes doive être conservée, donc ce n&#x27;est pas a priori le cas sur le diagramme final (on pourrait le forcer dans une direction ou dans l&#x27;autre, mais c&#x27;est un peu artificiel).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sociologie-de-comptoir&quot;&gt;Sociologie de comptoir&lt;&#x2F;h2&gt;
&lt;p&gt;Bon, soyons clairs, ce genre de graphique ne montre pas grand-chose de plus que ce qu&#x27;un observateur attentif de la vie politique française sait déjà. Mais ils ont l&#x27;avantage d&#x27;être jolis (contrairement aux observateurs attentifs de la vie politique française).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rouge-et-bleu-devant-marron-derriere&quot;&gt;Rouge et bleu devant, marron derrière&lt;&#x2F;h3&gt;
&lt;p&gt;Une des questions qui a motivé ce billet, c&#x27;est la (ré)apparition de l&#x27;extrême droite (et particulièrement du FN&#x2F;RN) en tant que force politique majeure pendant la Ve République. Et le graphique montre des choses intéressantes, je trouve.&lt;&#x2F;p&gt;
&lt;p&gt;Comme je l&#x27;avais fait remarquer dans le billet précédent, le parti des Le Pen est étonnamment stable et perd rarement des voix. Zemmour, qu&#x27;on présentait souvent comme son fossoyeur, par exemple, ne lui a piqué quasiment aucune voix en 2022. Sarko est le seul homme politique qui a réussi son OPA en récupérant un nombre substantiel de voix du FN en 2007, mais il les a (partiellement) reperdues à l&#x27;élection suivante (et a probablement contribué à normaliser le FN par la même occasion, mais j&#x27;éditorialise là).&lt;&#x2F;p&gt;
&lt;p&gt;Donc ma question, c&#x27;est d&#x27;où viennent ces voix. Pour qui votaient les gens qui votent pour le FN maintenant ? On en trouve des traces sur le graphique principal ; à chaque élection pré-2002, il y a des échappées vers le FN depuis Giscard, Mitterrand, Chirac ou Marchais. Mais pour avoir une idée un peu plus précise, on peut regarder l&#x27;évolution des votes non pas d&#x27;année en année, mais sur une période plus longue, par exemple de 1981 (où le FN était encore négligeable, au point que Le Pen n&#x27;avait pas réussi à se qualifier au premier tour) au second tour de 2002 (pays de meeeeerde).&lt;&#x2F;p&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1981_2002.html&quot; height=&quot;350&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Je ne pense pas qu&#x27;il faille attribuer trop de crédit à cette image. 20 ans, c&#x27;est une génération, un quart des votants qui se renouvelle, mais on peut quand même dire que le FN ne s&#x27;est pas formé sur le dos d&#x27;un seul parti (ou groupe politique). Ni de gauche, ni de droite, mais quand même carrément à droite. Ce qui serait amusant à regarder maintenant, c&#x27;est ce qui se passe avant &#x27;39, à quel point l&#x27;électorat de Le Pen est différent de l&#x27;électorat de Boulanger ou de Doriot. Potentiellement pour un prochain billet (d&#x27;ici 2 ans donc).&lt;&#x2F;p&gt;
&lt;p&gt;De nos jours, le RN est relativement stable, et quand il réussit à récupérer des électeurs au second tour, c&#x27;est ceux de la droite (Fillon en 2017 par exemple, voir la section suivante). La &amp;quot;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Horseshoe_theory&quot;&gt;théorie du fer à cheval&lt;&#x2F;a&gt;&amp;quot; est largement fausse de ce point de vue là.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;c-est-reparti-pour-un-tour&quot;&gt;C&#x27;est reparti pour un tour&lt;&#x2F;h3&gt;
&lt;p&gt;Allez, pour finir comme le billet précédent, voilà tous les flux entre les premiers et seconds tours de 1965 à 2022. Comme ils sont temporellement séparés par seulement deux semaines, beaucoup des problèmes décrits précédemment ne s&#x27;appliquent pas et les diagrammes sont particulièrement propres. On peut remarquer :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;2002 évidemment, où presque tous les Français qui n&#x27;avaient pas voté pour Le Pen sont allés mettre leur bulletin pour Chirac (avec St Josse et Mégret en exceptions). Beaucoup de Lepenistes du 1er tour se sont abstenus, par contraste.&lt;&#x2F;li&gt;
&lt;li&gt;La séparation du vote Le Pen en &#x27;88 et &#x27;95 en trois parties, une légère majorité qui vote à droite traditionnelle, et le reste qui se sépare en deux entre la gauche et l&#x27;abstention (je m&#x27;attendais clairement à beaucoup plus d&#x27;abstention). Dix ans plus tard, les voix du FN iront largement pour Sarkozy.&lt;&#x2F;li&gt;
&lt;li&gt;Le partage des voix de Fillon en 2017 et le contraste avec celles de Pécresse en 2022 (certes, il y en avait moins).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;div style=&quot;overflow-y:scroll; height:380px&quot;&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1er_2nd_1965.html&quot; height=&quot;440&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1er_2nd_1969.html&quot; height=&quot;440&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1er_2nd_1974.html&quot; height=&quot;440&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1er_2nd_1981.html&quot; height=&quot;440&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1er_2nd_1988.html&quot; height=&quot;440&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1er_2nd_1995.html&quot; height=&quot;440&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1er_2nd_2002.html&quot; height=&quot;440&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1er_2nd_2007.html&quot; height=&quot;440&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1er_2nd_2012.html&quot; height=&quot;440&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1er_2nd_2017.html&quot; height=&quot;440&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;1er_2nd_2022.html&quot; height=&quot;440&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;collegues&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;Principalement &amp;quot;et collègues&amp;quot;, j&#x27;imagine.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;metro&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;En France métropolitaine, ni outre-mer, ni Français de l&#x27;étranger.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;mieux&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;Dont je conseille la lecture, il est mieux écrit.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;eric&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;Calme-toi Éric.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;plainte&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;5&lt;&#x2F;sup&gt;
&lt;p&gt;Je me plains, mais c&#x27;est un superbe dataset. Le format est meilleur que celui utilisé par data.gouv.fr en plus.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;poids&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;6&lt;&#x2F;sup&gt;
&lt;p&gt;Un autre argument, c&#x27;est que cette équation donne trop d&#x27;importance (proportionnellement) aux petites communes, vu que seules les proportions des votes sont considérées.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;pre2015&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;7&lt;&#x2F;sup&gt;
&lt;p&gt;En vrai, c&#x27;est un argument pragmatique, les nouvelles régions sont un peu trop grandes et un peu trop différentes pour ce que je veux faire. Mais elles ont aussi des noms à la con.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;stat&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;8&lt;&#x2F;sup&gt;
&lt;p&gt;(Data not shown) comme ils disent, j&#x27;ai la flemme de faire des graphes propres. Oui, une longue discussion théorique pour finalement choisir une valeur au doigt mouillé, ce n&#x27;est pas ouf, mais c&#x27;est l&#x27;état de l&#x27;art.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;linéaire&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;9&lt;&#x2F;sup&gt;
&lt;p&gt;Avec le même nombre de paramètres. Parler de modèle linéaire est important ici, évidemment un gros modèle de deep learning peut certainement faire mieux que ça. Mais si on veut une interprétation en termes de flux, on &lt;em&gt;doit&lt;&#x2F;em&gt; utiliser un modèle linéaire, ce qui n&#x27;est pas forcément réaliste. On pourrait imaginer que chaque électeur de Phillipe Poutou réussit à convaincre 10 de ses voisins de voter pour lui à l&#x27;élection suivante (théorie du &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=o1ZjWBm9SyU&quot;&gt;Gros Poutou&lt;&#x2F;a&gt;), et donc la relation deviendrait quadratique.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;vote_contestataire&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;10&lt;&#x2F;sup&gt;
&lt;p&gt;Si je devais définir ce cliché journalistique un peu vide de sens des années 2000 qu&#x27;est le &amp;quot;vote contestataire&amp;quot;, je choisirais sûrement cette définition. Un vote qui ne dépend pas des votes précédents.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;abstention&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;11&lt;&#x2F;sup&gt;
&lt;p&gt;Je n&#x27;ai pas différencié abstention &#x2F; vote blanc cette fois, c&#x27;est dommage parce qu&#x27;il y a des dynamiques intéressantes (cf. billet précédent, les gens à droite ont tendance à voter blanc plutôt qu&#x27;à s&#x27;abstenir). Mais bon, pas suffisamment dommage pour me motiver à relancer tous les codes.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Le FCSC sans se fatiguer</title>
        <published>2022-05-08T00:00:00+00:00</published>
        <updated>2022-05-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://suboptimal.fr/2022-05-le-fcsc-sans-se-fatiguer/"/>
        <id>https://suboptimal.fr/2022-05-le-fcsc-sans-se-fatiguer/</id>
        
        <content type="html" xml:base="https://suboptimal.fr/2022-05-le-fcsc-sans-se-fatiguer/">&lt;p&gt;C&#x27;est le printemps, la saison du &lt;a href=&quot;https:&#x2F;&#x2F;www.france-cybersecurity-challenge.fr&quot;&gt;France Cybersecurity
Challenge&lt;&#x2F;a&gt; et donc de mon billet
de blog annuel, apparemment.  Organisé par l&#x27;&lt;a href=&quot;https:&#x2F;&#x2F;www.ssi.gouv.fr&quot;&gt;ANSSI&lt;&#x2F;a&gt;,
l&#x27;agence de cybersécurité française, le FCSC est l&#x27;occasion de sélectionner les
membres de l&#x27;équipe de France pour l&#x27;ECSC, le challenge européen.  Le challenge
est généreusement aussi ouvert aux vieux comme moi qui ne sont pas éligibles
pour raisons d&#x27;âge&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#âge&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;La diversité et l&#x27;originalité des épreuves en font facilement l&#x27;un de mes
concours préférés.  Je m&#x27;étais &lt;a href=&quot;https:&#x2F;&#x2F;suboptimal.fr&#x2F;2021-05-la-radio-pour-les-nuls&#x2F;&quot;&gt;beaucoup
amusé&lt;&#x2F;a&gt; avec les épreuves
hardware l&#x27;année dernière, et cette année j&#x27;ai eu à nouveau beaucoup de plaisir
dans de multiples catégories d&#x27;épreuves, mais surtout en crypto.&lt;&#x2F;p&gt;
&lt;p&gt;Comme c&#x27;est de tradition, je prolonge le plaisir en me prêtant au jeu des
&lt;em&gt;writeups&lt;&#x2F;em&gt; pour quelques unes des épreuves que j&#x27;ai le plus appréciées.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hardware&quot;&gt;Hardware&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;mommy-morse-77-resolutions&quot;&gt;Mommy Morse (77 résolutions)&lt;&#x2F;h3&gt;
&lt;p&gt;Il est demandé de transmettre un signal en morse, où les points et les traits
sont représentés par une « porteuse pure à une fréquence de 5 kHz », et les
espacements par une fréquence de 1 kHz.  Un signal d&#x27;exemple est fourni, et
comme j&#x27;aime bien visualiser, j&#x27;ai fait un graphe:&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;mommymorse_signal.svg&quot; alt=&quot;point trait point point ESPACE point etc.&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;point trait point point ESPACE point etc..&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Comme d&#x27;habitude, je trouve que tracer l&#x27;argument complexe du signal aide bien,
parce que dans ce cas il évolue de manière linéaire (modulo le modulo $2\pi$),
avec une pente qui dépend de la fréquence de la porteuse.  C&#x27;est exactement la
manière dont je m&#x27;y suis pris pour créer le signal dont j&#x27;avais besoin : j&#x27;ai
créé l&#x27;argument qu&#x27;il fallait, que j&#x27;ai mis dans une exponentielle complexe et
c&#x27;est passé.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;crypto&quot;&gt;Crypto&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;hash-ish-71-resolutions-et-khal-hash-10-resolutions&quot;&gt;Hash-ish (71 résolutions) et Khal Hash (10 résolutions)&lt;&#x2F;h3&gt;
&lt;p&gt;Sûrement mes deux épreuves préférées :)&lt;&#x2F;p&gt;
&lt;p&gt;Hash-ish donne un entier &lt;code&gt;H&lt;&#x2F;code&gt; et demande de trouver deux entiers &lt;code&gt;a&lt;&#x2F;code&gt; et &lt;code&gt;b&lt;&#x2F;code&gt; tels
que la valeur de &lt;code&gt;hash((a, b))&lt;&#x2F;code&gt; soit &lt;code&gt;H&lt;&#x2F;code&gt; (&lt;code&gt;hash&lt;&#x2F;code&gt; étant la fonction de hachage
par défaut de Python).&lt;&#x2F;p&gt;
&lt;p&gt;Mais comment ça fonctionne, &lt;code&gt;hash&lt;&#x2F;code&gt;, d&#x27;abord ?  Pour les tuples, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&#x2F;blob&#x2F;f298ba1f2712ad10530a30bb225548a6889820b5&#x2F;Objects&#x2F;tupleobject.c#L321&quot;&gt;son code est
là&lt;&#x2F;a&gt;
ou bien simplifié&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#grandsentiers&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; ici en Python (&lt;code&gt;PH1&lt;&#x2F;code&gt;, &lt;code&gt;PH2&lt;&#x2F;code&gt; et &lt;code&gt;PH5&lt;&#x2F;code&gt; sont
des constantes définies dans le code de CPython, et &lt;code&gt;MOD&lt;&#x2F;code&gt; vaut $2^{64}$) :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;myhash&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;integer_tuple&lt;&#x2F;span&gt;&lt;span&gt;: Tuple[int, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;]):
&lt;&#x2F;span&gt;&lt;span&gt;    acc = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH5
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;element &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;integer_tuple:
&lt;&#x2F;span&gt;&lt;span&gt;        acc = (acc + element * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH2&lt;&#x2F;span&gt;&lt;span&gt;) % &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MOD
&lt;&#x2F;span&gt;&lt;span&gt;        acc = ((acc &amp;lt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;31&lt;&#x2F;span&gt;&lt;span&gt;) | (acc &amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;33&lt;&#x2F;span&gt;&lt;span&gt;)) % &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MOD
&lt;&#x2F;span&gt;&lt;span&gt;        acc = (acc * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH1&lt;&#x2F;span&gt;&lt;span&gt;) % &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MOD
&lt;&#x2F;span&gt;&lt;span&gt;    acc = (acc + &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(t) ^ (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH5 &lt;&#x2F;span&gt;&lt;span&gt;^ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3527539&lt;&#x2F;span&gt;&lt;span&gt;)) % &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MOD
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;acc
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;C&#x27;est donc cette fonction qu&#x27;il faut inverser (on sait ce qu&#x27;elle doit
retourner, et on veut savoir quel argument lui donner pour qu&#x27;elle fasse ça).&lt;&#x2F;p&gt;
&lt;p&gt;Entre &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Z3Prover&#x2F;z3&quot;&gt;Z3&lt;&#x2F;a&gt;, un « prouveur de théorèmes »
développé par &lt;em&gt;Microsoft Research&lt;&#x2F;em&gt;.  Plus précisément, c&#x27;est un solveur
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Satisfiability_modulo_theories&quot;&gt;SMT&lt;&#x2F;a&gt;.  Sans
rentrer dans les détails, un solveur SMT est une généralisation d&#x27;un solveur
&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Boolean_satisfiability_problem&quot;&gt;SAT&lt;&#x2F;a&gt;.  Alors
qu&#x27;un solveur SAT détermine la satisfiabilité de formules logiques (avec des
portes logiques et des booléens), un solveur SMT détermine la satisfiabilité de
formules impliquant des objets plus complexes, comme des nombres ou des
structures de données.  Ici, c&#x27;est exactement ce qu&#x27;il faut : on a une formule
mathématique à résoudre sur les entiers modulo $2^{64}$.&lt;&#x2F;p&gt;
&lt;p&gt;Z3 possède des variables de type entier, mais vu qu&#x27;ici on a des opérations sur
des bits, j&#x27;ai préféré utiliser les &lt;code&gt;BitVec&lt;&#x2F;code&gt;s, qui sont comme leur nom
l&#x27;indique des tableaux de bits de taille fixée, et sur lesequels on peut quand
même faire des opérations arithmétiques facilement.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;solve&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;h&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    N = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;64
&lt;&#x2F;span&gt;&lt;span&gt;    s = z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Solver&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# a and b are the integers that we are looking for
&lt;&#x2F;span&gt;&lt;span&gt;    a = z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BitVec&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, N)
&lt;&#x2F;span&gt;&lt;span&gt;    b = z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BitVec&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, N)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# they need to be less than 2**60, or they don&amp;#39;t hash to themselves
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# ULT = Unsigned Less Than: `a &amp;lt; 2**60` doesn&amp;#39;t work unless we also add `0 &amp;lt;= a`
&lt;&#x2F;span&gt;&lt;span&gt;    s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ULT&lt;&#x2F;span&gt;&lt;span&gt;(a, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;    s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ULT&lt;&#x2F;span&gt;&lt;span&gt;(b, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# hashing the first integer
&lt;&#x2F;span&gt;&lt;span&gt;    acc0 = z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BitVec&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;acc0&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, N)
&lt;&#x2F;span&gt;&lt;span&gt;    s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(acc0 == &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH5 &lt;&#x2F;span&gt;&lt;span&gt;+ a * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH2&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    acc1 = z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BitVec&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;acc1&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, N)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# LShR stands for Logical Shift Right, &amp;gt;&amp;gt; being the arithmetic one
&lt;&#x2F;span&gt;&lt;span&gt;    s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(acc1 == (acc0 &amp;lt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;31&lt;&#x2F;span&gt;&lt;span&gt;) | z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;LShR&lt;&#x2F;span&gt;&lt;span&gt;(acc0, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;33&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;    acc2 = z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BitVec&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;acc2&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, N)
&lt;&#x2F;span&gt;&lt;span&gt;    s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(acc2 == acc1 * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# hashing the second integer
&lt;&#x2F;span&gt;&lt;span&gt;    acc3 = z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BitVec&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;acc3&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, N)
&lt;&#x2F;span&gt;&lt;span&gt;    s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(acc3 == acc2 + b * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH2&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    acc4 = z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BitVec&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;acc4&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, N)
&lt;&#x2F;span&gt;&lt;span&gt;    s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(acc4 == (acc3 &amp;lt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;31&lt;&#x2F;span&gt;&lt;span&gt;) | z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;LShR&lt;&#x2F;span&gt;&lt;span&gt;(acc3, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;33&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;    acc5 = z3.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BitVec&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;acc5&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, N)
&lt;&#x2F;span&gt;&lt;span&gt;    s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(acc5 == acc4 * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# the final hash value should be the one specified
&lt;&#x2F;span&gt;&lt;span&gt;    s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(h == acc5 + (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;^ (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH5 &lt;&#x2F;span&gt;&lt;span&gt;^ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3527539&lt;&#x2F;span&gt;&lt;span&gt;)))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# calling the solver
&lt;&#x2F;span&gt;&lt;span&gt;    sol = s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;(sol) == &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sat&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# a solution was found: return it
&lt;&#x2F;span&gt;&lt;span&gt;        model = s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;model&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(model.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(a).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;as_long&lt;&#x2F;span&gt;&lt;span&gt;(), model.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(b).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;as_long&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Appelée avec &lt;code&gt;h = 42&lt;&#x2F;code&gt; (au hasard), la fonction retourne le tuple suivant:
&lt;code&gt;(471024597674449489, 129304832457527322)&lt;&#x2F;code&gt;.  On vérifie :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;hash&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;471024597674449489&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;129304832457527322&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;42
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ça suffisait pour passer Hash-ish.  En revanche, le grand frère Khal Hash est
plus coriace.  En effet, il demande toujours de trouver un tuple d&#x27;entiers dont
le hash est une valeur donnée, mais avec la contrainte que les entiers doivent
être des caractères ascii (compris entre 0 et 127).  Or, deux entiers ne
suffisent plus : en effet, le hash étant une valeur de 64 bits, il faut environ
le même nombre de paramètres pour avoir une chance, soit environ 9 ou 10 entiers
(puisque chacun a 7 bits utilisables et que $64 &#x2F; 7 \approx 9$).  Z3 est
malheureusement dépassé par ces contraintes (le problème est beaucoup plus
&lt;em&gt;profond&lt;&#x2F;em&gt; : non seulement il y a plus de variables intermédiaires, mais aussi
le chemin est plus long entre le hash final et l&#x27;entrée à trouver).&lt;&#x2F;p&gt;
&lt;p&gt;J&#x27;ai donc écrit une version optimisée de la fonction &lt;code&gt;hash&lt;&#x2F;code&gt; en Rust, qui tourne
en une poignée de nanosecondes pour une entrée de 10 entiers.  C&#x27;est rapide !
Il ne reste plus qu&#x27;à essayer des tuples au hasard jusqu&#x27;à tomber sur la valeur
qu&#x27;on voulait.  Pas vrai ?  &lt;em&gt;Pas vrai ?&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;On peut essayer d&#x27;estimer combien de temps ça peut prendre : la probabilité de
tomber sur la valeur qu&#x27;on veut en hachant un tuple est environ $2^{-64}$.
Donc la probabilité de ne pas tomber dessus est de $1-2^{-64}$.  La probabilité
de ne jamais être tombé dessus après avoir haché $n$ tuples est donc
$(1-2^{-64})^n$.  Quelle valeur doit avoir $n$ pour que cette probabilité soit
assez basse, disons à $1&#x2F;2$ ?  La réponse est $n \approx 2^{64}$, à peu de
choses près.  Même avec la fonction optimisée, ça prendrait plus de 2000 ans de
hacher ce nombre de tuples&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#cestballot&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.  Le FCSC ne durant que 10 jours, il
faut trouver une autre technique.&lt;&#x2F;p&gt;
&lt;p&gt;La clé ici était de se rendre compte que les opérations de la fonction de hash
sont toutes inversibles.  En effet, reprenons la fonction ligne par ligne:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;acc = (acc + element * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH2&lt;&#x2F;span&gt;&lt;span&gt;) % &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MOD
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pour inverser cette addition, il suffit de retirer &lt;code&gt;element * PH2&lt;&#x2F;code&gt; modulo &lt;code&gt;MOD&lt;&#x2F;code&gt;
à &lt;code&gt;acc&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;acc = ((acc &amp;lt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;31&lt;&#x2F;span&gt;&lt;span&gt;) | (acc &amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;33&lt;&#x2F;span&gt;&lt;span&gt;)) % &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MOD
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Cette ligne réalise en fait une rotation des bits de &lt;code&gt;acc&lt;&#x2F;code&gt;, de 33 bits vers la
droite (ou 31 vers la gauche).  Pour l&#x27;inverser, il suffit donc de faire la
même rotation à l&#x27;envers.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;acc = (acc * &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH1&lt;&#x2F;span&gt;&lt;span&gt;) % &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MOD
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pour inverser cette multiplication, on multiplie &lt;code&gt;acc&lt;&#x2F;code&gt; par l&#x27;inverse modulaire
de &lt;code&gt;PH1&lt;&#x2F;code&gt; modulo &lt;code&gt;MOD&lt;&#x2F;code&gt;.  On peut calculer facilement cet inverse en python avec
la fonction &lt;code&gt;pow&lt;&#x2F;code&gt; du langage: &lt;code&gt;pow(PH1, -1, MOD)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;acc = (acc + &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(t) ^ (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PH5 &lt;&#x2F;span&gt;&lt;span&gt;^ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3527539&lt;&#x2F;span&gt;&lt;span&gt;)) % &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MOD
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;La dernière ligne est également facile à inverser puisqu&#x27;il s&#x27;agit encore juste
d&#x27;une addition.&lt;&#x2F;p&gt;
&lt;p&gt;Tout cela signifie qu&#x27;en partant de la valeur finale connue du hash, on peut
prendre les éléments du tuple à l&#x27;envers et revenir à la valeur initiale de
l&#x27;accumulateur &lt;code&gt;acc&lt;&#x2F;code&gt; (&lt;code&gt;PH5&lt;&#x2F;code&gt;).  Mais chercher dans un sens ou dans l&#x27;autre ne
change rien en termes de temps de calcul.  L&#x27;astuce est de chercher par les
deux bouts et de faire se retrouver la recherche au milieu.&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;meetinthemiddle.jpeg&quot; alt=&quot;&quot; width=&quot;100%&quot;&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Imaginons qu&#x27;on commence par le faisceau de droite, et qu&#x27;on enregistre tous
ses points d&#x27;arrivée.  Maintenant, le faisceau de gauche peut se raccorder non
plus juste au point final, mais à chacun des $n$ points du faisceau de droite.
On arrive ainsi à $n \approx 2^{32}$ au lieu des $2^{64}$ de la première
méthode, pour la même probabilité de succès.  C&#x27;est un sacré progrès, et ça se
calcule à présent en quelques secondes ou minutes au lieu de miliers
d&#x27;années&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#anniversaires&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;En pratique, j&#x27;ai calculé tous les tuples de taille 4 en partant d&#x27;un côté, et
cherché les tuples de taille 6 en partant de l&#x27;autre côté, et je suis tombé sur
une solution en une ou deux minutes.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;t-rex-67-resolutions&quot;&gt;T-Rex (67 résolutions)&lt;&#x2F;h3&gt;
&lt;p&gt;Je passe des détails, mais en gros on nous donnait le résultat de
$F^{31337}(k)$ où $F$ est l&#x27;application $x \mapsto (2x+1)x$ modulo $2^{128}$,
et on veut remonter à $k$ qui est la clé de chiffrement du message.  Après
m&#x27;être rendu compte que $F$ était bijective, j&#x27;ai passé un bon moment à essayer
de l&#x27;inverser à la main sans succès, avant de réaliser que je pouvais utiliser
là aussi Z3 (31337 fois de suite).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;gaston-la-paffe-29-resolutions&quot;&gt;Gaston La Paffe (29 résolutions)&lt;&#x2F;h3&gt;
&lt;p&gt;On avait ici un algorithme de signature à base de polynomes, qui fonctionnait
de la manière suivante (là aussi, j&#x27;omets des détails) :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s1&lt;&#x2F;code&gt; et &lt;code&gt;s2&lt;&#x2F;code&gt; des polynomes choisis au hasard, constituent la clé privée ;&lt;&#x2F;li&gt;
&lt;li&gt;On nous donne &lt;code&gt;a&lt;&#x2F;code&gt; et &lt;code&gt;t&lt;&#x2F;code&gt;, polynomes tels que &lt;code&gt;t = a*s1 + s2&lt;&#x2F;code&gt; ;&lt;&#x2F;li&gt;
&lt;li&gt;Pour signer un message &lt;code&gt;m&lt;&#x2F;code&gt;, &lt;code&gt;y1&lt;&#x2F;code&gt; et &lt;code&gt;y2&lt;&#x2F;code&gt; sont d&#x27;abord générés aléatoirement.  Puis on calcule &lt;code&gt;c = H(a*y1 + y2, m)&lt;&#x2F;code&gt; où &lt;code&gt;H&lt;&#x2F;code&gt; est une fonction à sens unique. Enfin, on retourne &lt;code&gt;z1 = s1*c + y1&lt;&#x2F;code&gt; et &lt;code&gt;z2 = s2*c + y2&lt;&#x2F;code&gt;, ainsi que &lt;code&gt;y1&lt;&#x2F;code&gt; « par erreur ».&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Quelques calculs permettent de se rendre compte que connaître &lt;code&gt;y1&lt;&#x2F;code&gt; permet de
trouver aussi &lt;code&gt;y2&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;y2 = a*(z1 - y1) + z2 - t*c
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;et qu&#x27;à partir de là, on a les équations&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;s1 * c = z1 - y1
&lt;&#x2F;span&gt;&lt;span&gt;s2 * c = z2 - y2
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;dans lesquelles on connaît toutes les variables à part les secrets &lt;code&gt;s1&lt;&#x2F;code&gt; et &lt;code&gt;s2&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Pour trouver les coefficients des secrets, j&#x27;ai choisi d&#x27;utiliser &lt;code&gt;ortools&lt;&#x2F;code&gt;,
une suite de solveurs développée par Google.  Z3 aurait peut-être pu aussi
fonctionner, mais il s&#x27;agissait ici en fait d&#x27;un problème de programmation
linéaire sur les entiers, ce sur quoi &lt;code&gt;ortools&lt;&#x2F;code&gt; est plus efficace.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;side-channels-and-fault-attacks&quot;&gt;Side Channels and Fault Attacks&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;never-skip-class-nor-multiplication-85-resolutions&quot;&gt;Never Skip Class Nor Multiplication (85 résolutions)&lt;&#x2F;h3&gt;
&lt;p&gt;On avait accès à un oracle RSA chiffrant des messages choisis, à l&#x27;aide d&#x27;une
fonction d&#x27;exponentiation modulaire « buguée » de manière à ce qu&#x27;on puisse
choisir quelle multiplication elle allait sauter.  L&#x27;objectif était de
déterminer la clé privée $d$ (l&#x27;exposant de l&#x27;opération $c = m^d \mod n$).&lt;&#x2F;p&gt;
&lt;p&gt;La question à se poser est la suivante : Si je saute la multiplication du bit
$k$ de la clé $d$, quel est l&#x27;exposant $d&#x27;$ qui a alors été utilisé ?&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;    Exposant d initial                       Exposant d&amp;#39; réel
&lt;&#x2F;span&gt;&lt;span&gt;N-1          K            0    skip K    N-1          K            0
&lt;&#x2F;span&gt;&lt;span&gt;[    dH    ] 0 [    dL    ]    -----&amp;gt;    [    dH    ] 0 [    dL    ]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;N-1          K            0    skip K    N-1          K            0
&lt;&#x2F;span&gt;&lt;span&gt;[    dH    ] 1 [    dL    ]    -----&amp;gt;    [    dH    ] 0 [    dL    ]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On remarque facilement que sauter un bit nul ne modifie pas l&#x27;exposant, alors
que sauter un bit non-nul le change.  Il suffit alors de demander à chiffrer le
même message en sautant tous les bits un par un, et de comparer ces résultats à
la situation où on ne saute aucun bit.  On a alors une correspondance directe
entre les résultats et la clé.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;never-skip-class-nor-squaring-70-resolutions&quot;&gt;Never Skip Class Nor Squaring (70 résolutions)&lt;&#x2F;h3&gt;
&lt;p&gt;Même principe, mais la fonction est buguée de manière à sauter l&#x27;étape de mise
au carré.  La question est la même : si on saute le bit $K$ de l&#x27;exposant $d$,
quel exposant $d&#x27;$ a-t-on alors vraiment utilisé ?&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;    Exposant d initial                       Exposant d&amp;#39; réel
&lt;&#x2F;span&gt;&lt;span&gt;N-1          K            0    skip K    N-2        K            0
&lt;&#x2F;span&gt;&lt;span&gt;[    dH    ] 0 [    dL    ]    -----&amp;gt;    [    dH    ] [    dL    ]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;N-1          K            0    skip K    N-2        K            0
&lt;&#x2F;span&gt;&lt;span&gt;[    dH    ] 1 [    dL    ]    -----&amp;gt;    [  dH + 1  ] [    dL    ]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Grâce à quelques dessins dans ce genre, on se rend compte que peu importe la
valeur du bit $K$, on a toujours $d - d&#x27; = d_H \cdot 2^K$.  Cette observation
permet de déterminer $d$ bit par bit.  En effet, si on appelle $c$ la vraie
valeur de $m^d \mod n$, en sautant le bit $N-2$ on obtient pour $m^{d&#x27;}$ deux
valeurs possibles selon que le bit $N-1$ est nul ou non : soit $c$ (s&#x27;il est
nul), soit $c\cdot m^{-2^{N-2}}$ (s&#x27;il ne l&#x27;est pas).  Armé de cette
information, on saute alors le bit $N-3$ pour déterminer la valeur du bit
$N-2$, etc.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;misc&quot;&gt;Misc&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;guess-me-too-68-resolutions&quot;&gt;Guess Me Too (68 résolutions)&lt;&#x2F;h3&gt;
&lt;p&gt;Un nombre de 128 bits est tiré au hasard et il faut le deviner.  On ne peut
poser que 136 questions de la forme « Parmi les bits numéros $x_0$, $x_1$,
$x_2$, etc. du nombre secret, quelle est la parité du nombre de bits non
nuls ? » (en choisissant les $x_i$).  De plus, on sait qu&#x27;une des réponses sera
fausse mais on ne sait pas laquelle.&lt;&#x2F;p&gt;
&lt;p&gt;Il semble logique de commencer par demander les 128 bits un à un.  On a déjà
presque 6% de chance d&#x27;avoir deviné le nombre (si la réponse incorrecte tombe
dans les 8 autres questions), donc on pourrait déjà s&#x27;arrêter là et faire
plusieurs tentatives jusqu&#x27;à ce que ça marche (17 fois en moyenne).&lt;&#x2F;p&gt;
&lt;p&gt;Pour deviner le nombre à coup sûr, il faut faire de la correction d&#x27;erreur.&lt;&#x2F;p&gt;
&lt;p&gt;On commence par demander la parité de la somme de tous les bits, et on compare
avec le résultat des 128 premières questions.  Si les résultats concordent, on
sait qu&#x27;on n&#x27;est pas encore tombé sur la réponse incorrecte.  Sinon, c&#x27;est
qu&#x27;il y a une erreur soit dans les questions individuelles, soit dans la
question de contrôle.  Pour la déterminer, on demande alors la parité de la
somme de tous les bits impairs, que l&#x27;on compare à nouveau au résultat des
questions individuelles.  On sait que le résultat de cette question sera
correct, puisqu&#x27;on a déjà détecté une erreur et qu&#x27;il ne peut y en avoir qu&#x27;une
seule.  Si les résultats concordent, on sait que l&#x27;erreur est dans les bits
pairs, sinon c&#x27;est qu&#x27;elle est dans les bits impairs.  On peut alors continuer
à préciser la position de l&#x27;erreur en demandant la parité de la somme des bits
impairs des bits suspects d&#x27;être erronés, jusqu&#x27;à ce qu&#x27;il n&#x27;y ait plus qu&#x27;une
seule possibilité.&lt;&#x2F;p&gt;
&lt;p&gt;En pratique, toutes les questions doivent être posées en même temps au début.
Qu&#x27;à cela ne tienne, les huit questions supplémentaires à poser qui
fonctionnent dans tous les cas sont les suivantes :&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
&lt;&#x2F;span&gt;&lt;span&gt;10101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
&lt;&#x2F;span&gt;&lt;span&gt;11001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100110011001100
&lt;&#x2F;span&gt;&lt;span&gt;11110000111100001111000011110000111100001111000011110000111100001111000011110000111100001111000011110000111100001111000011110000
&lt;&#x2F;span&gt;&lt;span&gt;11111111000000001111111100000000111111110000000011111111000000001111111100000000111111110000000011111111000000001111111100000000
&lt;&#x2F;span&gt;&lt;span&gt;11111111111111110000000000000000111111111111111100000000000000001111111111111111000000000000000011111111111111110000000000000000
&lt;&#x2F;span&gt;&lt;span&gt;11111111111111111111111111111111000000000000000000000000000000001111111111111111111111111111111100000000000000000000000000000000
&lt;&#x2F;span&gt;&lt;span&gt;11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Si on regarde par exemple la ligne &lt;code&gt;11001100...&lt;&#x2F;code&gt;, elle contient bien tous les
bits impairs des bits impairs, ainsi que tous les bits impairs des bits pairs,
et ainsi de suite.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;coda&quot;&gt;Coda&lt;&#x2F;h2&gt;
&lt;p&gt;Z3 et &lt;code&gt;ortools&lt;&#x2F;code&gt; se sont avérés être un arsenal efficace cette année, me
permettant de résoudre 5 challenges (dont 4 de crypto) sans trop d&#x27;effort.  Ils
rendent aussi service en dehors du FCSC : ce sont des outils à connaître.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;âge&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;et qui ne l&#x27;ont d&#x27;ailleurs jamais été :&#x27;(&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;grandsentiers&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;notamment en profitant du fait que &lt;code&gt;hash(n) == n&lt;&#x2F;code&gt; pour tout &lt;code&gt;n&lt;&#x2F;code&gt; entier inférieur à $2^{60}$&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;cestballot&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;Et au bout du compte, on aurait encore une chance sur deux de ne pas avoir trouvé la bonne valeur…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;anniversaires&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;C&#x27;est un exemple d&#x27;application du &lt;a href=&quot;https:&#x2F;&#x2F;fr.wikipedia.org&#x2F;wiki&#x2F;Paradoxe_des_anniversaires&quot;&gt;paradoxe des anniversaires&lt;&#x2F;a&gt;, un grand classique de la cryptanalyse, qui, en échange de la mémoire utilisée pour stocker des hashes, divise l&#x27;exposant du nombre de calculs à faire par deux.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Élections</title>
        <published>2022-04-12T00:00:00+00:00</published>
        <updated>2022-04-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://suboptimal.fr/2022-04-elections/"/>
        <id>https://suboptimal.fr/2022-04-elections/</id>
        
        <content type="html" xml:base="https://suboptimal.fr/2022-04-elections/">&lt;p&gt;Les élections des dernières années ont, comme on dit dans les titres de journaux, « bouleversé le paysage politique français ». En particulier, celle du week-end dernier a réduit à peau de chagrin les deux partis politiques de papa, vu l&#x27;apparition d&#x27;un parti d&#x27;extrême-droite supplémentaire (comme si on n&#x27;en avait pas assez) et rendu le monde un peu plus triste&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#triste&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Les votes ont changé donc, mais les électeurs eux sont globalement restés les mêmes. Certes, en 10 ans, 7 millions de petits jeunes ont signé dans la réglette pour la première fois, et 5 millions de petits vieux sont devenus des ex-votants, mais il reste plus de 30 millions de braves gens qui ont poussé leur bulletin dans la fente en 2012, 2017 et 2022. L&#x27;objet qui va nous intéresser ici, c&#x27;est le nombre d&#x27;entre eux qui ayant voté pour un candidat X en 2017 ont voté pour un candidat Y en 2022, le &lt;em&gt;flux d&#x27;électeurs&lt;&#x2F;em&gt;. &lt;&#x2F;p&gt;
&lt;p&gt;Une méthode pour étudier ces flux d&#x27;une élection à l&#x27;autre consiste à regarder comment le vote évolue dans chacune des 34 970 communes. Les résultats de chaque bureau de vote sont publiés en ligne ce qui brise probablement un peu le concept de vote à bulletin secret, particulièrement pour les habitants de Pétoise-sur-Sère en Ardèche, mais est bien pratique pour faire des statistiques. Par exemple, si Pétoise-sur-Sère a eu 90% de votes pour Hamon et 10% de votes pour Le Pen en 2017, et qu&#x27;en 2022 on observe 90% de votes pour Mélenchon et 10% de votes pour Zemmour, on pourra faire l&#x27;hypothèse raisonnable d&#x27;un transfert des voix Hammon → Mélenchon et Le Pen → Zemmour. Si un schéma similaire se dessine pour les communes voisines de Pucave-sur-Sère, Pelos-sur-Sère et Palavas-sur-flots, on peut quantifier ces différents flux et faire un joli graphe.&lt;&#x2F;p&gt;
&lt;p&gt;C&#x27;est l&#x27;idée au moins, en pratique c&#x27;est un peu plus pénible, mais je laisse les détails techniques pour la dernière partie, et je commence par le résultat.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;resultats&quot;&gt;Résultats&lt;&#x2F;h3&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;evolution_2012_2022.html&quot; height=&quot;400&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Le diagramme&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#sankey&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; est assez clair, il montre l&#x27;évolution des voix pour chaque candidat en 2012, 2017 et 2022. On y voit:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;le PS se déchirer entre Macron, Hamon et Mélenchon, puis les voix d&#x27;Hamon rejoindre Mélenchon (et quelques-unes aller vers Hidalgo, mais j&#x27;ai viré les traits trop petits pour que ça soit plus lisible, désolé Annie) ;&lt;&#x2F;li&gt;
&lt;li&gt;le léger, presque imperceptible, virage à droite de Manu ;&lt;&#x2F;li&gt;
&lt;li&gt;l&#x27;explosion du LR entre Zemmour, Pécresse et Macron. Zemmour qui ne récupère d&#x27;ailleurs presque aucune voix de Marine, on est fidèle au RN (&lt;em&gt;ein Reich, ein Führer&lt;&#x2F;em&gt;, ou quelque chose comme ça, j&#x27;imagine) ;&lt;&#x2F;li&gt;
&lt;li&gt;et bien d&#x27;autres merveilles de la vie politique française.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Je ne vais pas me lancer dans une analyse politique sauvage, je suis pas éditorialiste, heureusement. Et discuter technique c&#x27;est moins marrant mais c&#x27;est important aussi. Parce que technique il y a, ce genre de graphe fait beaucoup d&#x27;hypothèses sans les nommer.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;methode&quot;&gt;Méthode&lt;&#x2F;h3&gt;
&lt;p&gt;Dans chaque commune $c$, il y a eu respectivement $N_{c, i}$ et $M_{c, i}$ votes pour le candidat $i$ lors de deux élections. Ce que l&#x27;on cherche, c&#x27;est $F^{\mathrm{emp.}}_{i, j}$, la proportion de votants du candidat $i$ lors de la première élection qui ont voté pour $j$ lors de la seconde. Ici l&#x27;abstention et le vote blanc&#x2F;nul sont considérés comme des candidats comme les autres.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#honn&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;L&#x27;approche la plus simple consiste à minimiser sur l&#x27;ensemble des communes la différence entre le nombre de votants réels pour $i$ à la seconde élection $M_{c, i}$ et le nombre de votants estimés, $\sum_{i} N_{c, j} F_{j, i}$. Explicitement, cela revient à calculer la norme de $M - N.F$ (en notation matricielle). Additionellement, on rajoute des contraintes sur les sommes des lignes et colonnes de $F$, pour que le modèle n&#x27;invente pas des votants (la primaire LR est finie), ces contraintes sont assez lâches, pour ne pas rendre le modèle insoluble.&lt;&#x2F;p&gt;
&lt;p&gt;Il y a une hypothèse cachée derrière cette méthode. Pour que ce calcul ne soit pas biaisé, il faut que toutes les communes aient les mêmes valeurs de flux (ou plus précisément la même distribution de flux sous-jacente). On imagine bien que Paris et Pétoise-sur-Sère ont des populations suffisamment différentes pour que ce ne soit pas le cas. Si on limite l&#x27;ensemble des communes sur lesquelles on optimise, on peut se restreindre à un département. Disons l&#x27;Hérault par exemple, et le Finistère pour équilibrer la pluviométrie.&lt;&#x2F;p&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;evolution_2017_2022_herault.html&quot; height=&quot;525&quot; width=&quot;48%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;evolution_2017_2022_finistere.html&quot; height=&quot;525&quot; width=&quot;48%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Ce ne sont pas des départements dramatiquement distincts, mais on observe des différences notables dans les matrices de flux. Le vote Fillon s&#x27;est beaucoup plus reporté sur Macron dans le Finistère par exemple et beaucoup de macronistes de gauche ont découvert que c&#x27;était dur à tenir comme position. C&#x27;est beaucoup moins vrai dans l&#x27;Hérault. Regrouper ces populations avant de minimiser ne fait pas trop sens&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#oui&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;La méthode honorable consisterait à associer les différentes communes en suivant des critères sociologiques, puis à minimiser indépendamment chacun de ces ensembles de communes et à regrouper les matrices de flux finales (en pondérant) pour faire une jolie figure. Une seconde option, plus élégante théoriquement, mais aussi moins précise, consisterait à inférer approximativement ces ensembles de communes à travers leurs habitudes de vote. Par exemple avec un algorithme de cluster. Ou en minimisant les contraintes globalement tout en autorisant la présence de plusieurs matrices de flux différentes. Il y a plein d&#x27;options donc. Évidemment j&#x27;ai choisi la pire et ai simplement mesuré les matrices de flux pour chaque département pour les regrouper au dernier moment.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#futur&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Un autre problème majeur mais difficilement adressable, c&#x27;est que les Français ont la mauvaise habitude de déménager. Et en particulier de déménager jeune. Cette manie désagréable est probablement responsable des échanges de flux Mélenchon ↔ abstention, ou de l&#x27;éclatement des votes Poutou et Arthaud. &lt;&#x2F;p&gt;
&lt;p&gt;Le dernier problème réglé à la hachette, c&#x27;est les métropoles. Paris, Lyon et Marseille sont particulièrement pénibles. La taille de la commune empêche d&#x27;avoir une bonne précision sur les flux, les définitions des arrondissements changent suivant la source considérée et le concept de circonscription m&#x27;échappe. Ça m&#x27;énerve. Et quand ça m&#x27;énerve je supprime. J&#x27;espère que ça n&#x27;introduit pas trop de biais, Neuilly compensera pour le XVIe et la Seine-Saint-Denis aidera le XXe à se sentir représenté quand même. C&#x27;est sûr que 4 millions d&#x27;habitants en moins ça pique un peu, mais j&#x27;assume totalement comme dit l&#x27;autre. &lt;&#x2F;p&gt;
&lt;p&gt;Numériquement, les problèmes des moindres carrés ont été résolus à l&#x27;aide de Gurobi&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#marteau&quot;&gt;6&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; et les jolis plots viennent de plotly&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#soup&quot;&gt;7&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Les données sont toutes publiées de manière assez hasardeuse par data.gouv.fr, avec des noms de colonnes dans tous les sens. La donnée élevée en liberté c&#x27;est pas encore ça en France. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;Je soupçonne que ces données communales sont un peu sous-employées par les instituts de sondage, elles donnent une image assez précise pourtant. Les Français ont des idées politiques remarquablement stable sur 8 ans, même si les partis politiques qui les représentent ont changé. Pays de merde tiens. Bon sur ce, voilà le flux 1er tour ⇒ 2ème tour de 2017. Tirez-en ce que vous voulez. &lt;&#x2F;p&gt;
&lt;iframe id=&quot;igraph&quot; scrolling=&quot;no&quot; style=&quot;border:none;&quot; seamless=&quot;seamless&quot; src=&quot;evolution_1er_2nd.html&quot; height=&quot;525&quot; width=&quot;100%&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;triste&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;C&#x27;est le thème de cette année globalement.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;sankey&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;Ça s&#x27;appelle un diagramme de Sankey d&#x27;après internet, du nom d&#x27;un capitaine qui aurait fait le premier en 1898. Mais la &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Sankey_diagram#&#x2F;media&#x2F;File:Minard.png&quot;&gt;jolie mais imbitable carte de Minard&lt;&#x2F;a&gt;, qui date de 1869, y ressemble pas mal aussi.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;honn&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;Bien qu&#x27;ils soient honnêtes, eux.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;oui&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;Oui c&#x27;est volontaire.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;futur&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;5&lt;&#x2F;sup&gt;
&lt;p&gt;Ok, je songe assez fort à en faire un autre post de blog, d&#x27;ici un an ou deux, c&#x27;est un bon rythme ça, un post par an.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;marteau&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;6&lt;&#x2F;sup&gt;
&lt;p&gt;C&#x27;est du least square avec des contraintes linéaires, c&#x27;est clairement du vice de sortir Gurobi pour ça. Mais quand on a un marteau...&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;soup&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;7&lt;&#x2F;sup&gt;
&lt;p&gt;Qui est imbitable à utiliser, avec une doc encore pire que celle de matplotlib, mais ne crachons pas dans la soupe, le résultat final est joli.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>La radio pour les nuls</title>
        <published>2021-05-15T00:00:00+00:00</published>
        <updated>2021-05-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://suboptimal.fr/2021-05-la-radio-pour-les-nuls/"/>
        <id>https://suboptimal.fr/2021-05-la-radio-pour-les-nuls/</id>
        
        <content type="html" xml:base="https://suboptimal.fr/2021-05-la-radio-pour-les-nuls/">&lt;p&gt;J&#x27;ai eu le plaisir de participer pour la deuxième année au &lt;a href=&quot;https:&#x2F;&#x2F;www.france-cybersecurity-challenge.fr&#x2F;&quot;&gt;France
Cybersecurity Challenge&lt;&#x2F;a&gt;
organisé par l&#x27;&lt;a href=&quot;https:&#x2F;&#x2F;www.ssi.gouv.fr&quot;&gt;ANSSI&lt;&#x2F;a&gt;, l&#x27;agence de
cybersécurité française.  Parmi les épreuves proposées, une des
catégories qui m&#x27;attirait le plus cette année était la catégorie
&amp;quot;hardware&amp;quot;, dédiée à l&#x27;analyse de signaux radio.  Le truc, c&#x27;est
que je n&#x27;y connais absolument rien, et je n&#x27;avais pas le temps
d&#x27;apprendre à me servir de &lt;a href=&quot;https:&#x2F;&#x2F;www.gnuradio.org&#x2F;&quot;&gt;GNURadio&lt;&#x2F;a&gt;,
le logiciel de référence du domaine.  J&#x27;ai quand même
réussi à être la première personne à valider &lt;a href=&quot;https:&#x2F;&#x2F;www.france-cybersecurity-challenge.fr&#x2F;challenges#B.A.%20BA-52&quot;&gt;B.A.
BA&lt;&#x2F;a&gt;
(137 résolutions &#x2F; 1732 joueurs), le deuxième à valider &lt;a href=&quot;https:&#x2F;&#x2F;www.france-cybersecurity-challenge.fr&#x2F;challenges#Phase%20%C3%A0%20phase-53&quot;&gt;Phase à
phase&lt;&#x2F;a&gt;
(20 résolutions), et à avoir presque résolu
&lt;a href=&quot;https:&#x2F;&#x2F;www.france-cybersecurity-challenge.fr&#x2F;challenges#Zodiaque-59&quot;&gt;Zodiaque&lt;&#x2F;a&gt;
(6 résolutions), tout ça en une soirée.&lt;&#x2F;p&gt;
&lt;p&gt;Malgré son titre, ce post n&#x27;est pas une explication de la radio
logicielle, parce que je n&#x27;y connais toujours rien, c&#x27;est juste un
writeup de ces trois épreuves sans autre prérequis que d&#x27;être à
l&#x27;aise en Python.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;b-a-ba&quot;&gt;B.A. BA&lt;&#x2F;h1&gt;
&lt;p&gt;On a un fichier &lt;code&gt;challenge.iq&lt;&#x2F;code&gt; à analyser.  IQ, qu&#x27;est-ce que
c&#x27;est que ça ?  Google donne une page de la doc de
&lt;a href=&quot;https:&#x2F;&#x2F;pysdr.org&#x2F;content&#x2F;iq_files.html&quot;&gt;PySDR&lt;&#x2F;a&gt;, qui dit que c&#x27;est des
valeurs complexes qui décrivent le signal.  Admettons.  Ils disent
aussi comment les lire avec &lt;code&gt;numpy&lt;&#x2F;code&gt; (tant mieux parce qu&#x27;on n&#x27;a pas le
temps d&#x27;apprendre PySDR non plus) :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;iqs = np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fromfile&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;challenge.iq&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, np.complex64)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(iqs.shape)    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# (3675100,)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Bon, faisons un graphe.  Ce sont des valeurs complexes donc on va tracer
la partie réelle, imaginaire, le module et l&#x27;argument au cas où
certaines représentations seraient plus intéressantes que d&#x27;autres.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;fig, axs = plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;subplots&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sharex&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;axs[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;real&lt;&#x2F;span&gt;&lt;span&gt;(iqs))
&lt;&#x2F;span&gt;&lt;span&gt;axs[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;imag&lt;&#x2F;span&gt;&lt;span&gt;(iqs))
&lt;&#x2F;span&gt;&lt;span&gt;axs[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;abs&lt;&#x2F;span&gt;&lt;span&gt;(iqs))
&lt;&#x2F;span&gt;&lt;span&gt;axs[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;angle&lt;&#x2F;span&gt;&lt;span&gt;(iqs))
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;show&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;figure&gt;
    &lt;img src=&quot;baba_plot_complex.svg&quot; alt=&quot;En zoomant un peu, ça ressemble à un signal&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;En zoomant un peu, ça ressemble à un signal.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Donc il semblerait que ce soient des nombres soit grands, soit petits.
Le module semble le plus pratique pour décider de ça.  On va choisir de dire
qu&#x27;au-dessus de 0.75 c&#x27;est grand, et en dessous c&#x27;est petit :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;discrete = np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;abs&lt;&#x2F;span&gt;&lt;span&gt;(iqs) &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.75
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Maintenant, on a un tableau rempli de &lt;code&gt;True&lt;&#x2F;code&gt; et de &lt;code&gt;False&lt;&#x2F;code&gt;, et sur
le graphe précédent ça a l&#x27;air de passer de l&#x27;un à l&#x27;autre quand
même assez rarement.  On va calculer les longueurs des groupes
consécutifs de &lt;code&gt;True&lt;&#x2F;code&gt; et &lt;code&gt;False&lt;&#x2F;code&gt; avec &lt;code&gt;groupby&lt;&#x2F;code&gt;, une fonction du
package &lt;code&gt;itertools&lt;&#x2F;code&gt; :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;signals = [(value, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span&gt;(group)))
&lt;&#x2F;span&gt;&lt;span&gt;           &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;value, group &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;it.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;groupby&lt;&#x2F;span&gt;&lt;span&gt;(discrete)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(signals[:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# [(False, 4994), (True, 150), (False, 1000), (True, 500), (False, 999)]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Regardons quelles sont les durées les plus fréquentes, pour les deux valeurs :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;sizesT = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Counter&lt;&#x2F;span&gt;&lt;span&gt;(l &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;v, l &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;signals &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;v)
&lt;&#x2F;span&gt;&lt;span&gt;sizesF = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Counter&lt;&#x2F;span&gt;&lt;span&gt;(l &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;v, l &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;signals &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;not v)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(sizesT)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Counter({150: 820, 500: 506, 499: 161, 149: 114, 151: 19, 501: 2})
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(sizesF)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Counter({1000: 518, 999: 475, 3498: 472, 3499: 154, 3497: 2, 4994: 1, 3489: 1})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Donc les valeurs &lt;code&gt;True&lt;&#x2F;code&gt; sont là en paquets de 150±1 et 500±1, et
les valeurs &lt;code&gt;False&lt;&#x2F;code&gt; en paquets de 1000±1 et 3500±2.  Le sujet parle
de Morse, donc probablement 150 c&#x27;est un point, 500 un trait, 1000 ça
doit être la séparation entre les symboles, et 3500 c&#x27;est entre deux
lettres.  Il reste donc à traduire ça en texte :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;m = &amp;#39;&amp;#39;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;join&lt;&#x2F;span&gt;&lt;span&gt;((&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;l &amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;300 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;v &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39; &amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;l &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2000 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;v, l &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;signals)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(m)    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;#  .-.. . -.-. --- -.. . -- --- etc.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On copie-colle ça dans &lt;a href=&quot;https:&#x2F;&#x2F;www.dcode.fr&#x2F;code-morse&quot;&gt;dcode&lt;&#x2F;a&gt; et on a le
premier flag.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;phase-a-phase&quot;&gt;Phase à phase&lt;&#x2F;h1&gt;
&lt;p&gt;Encore un fichier &lt;code&gt;challenge.iq&lt;&#x2F;code&gt;.  Cette fois, on sait déjà le lire
et faire un premier graphe.&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;pap_plot_complex.svg&quot; alt=&quot;Cette fois-ci c&#x27;est un peu moins clair&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Cette fois-ci c&#x27;est un peu moins clair.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Cette fois-ci, le module semble constant, c&#x27;est l&#x27;argument qui fait
des choses étranges.  Même son de cloche pour les parties réelle et
imaginaire qui semblent décrire une sinusoïde qui change de phase
brutalement, et assez souvent.  Comme c&#x27;est le titre de l&#x27;épreuve,
j&#x27;imagine que les données sont encodées ici dans ces changements de phase.&lt;&#x2F;p&gt;
&lt;p&gt;En tout cas on n&#x27;est pas encore très avancé.  La page de tout à
l&#x27;heure suggérait une autre représentation, en &amp;quot;constellation&amp;quot;.  Allez,
essayons ça :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;real&lt;&#x2F;span&gt;&lt;span&gt;(iqs), np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;imag&lt;&#x2F;span&gt;&lt;span&gt;(iqs), &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;show&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;figure&gt;
    &lt;img src=&quot;pap_plot_constellation.svg&quot; alt=&quot;Ouah ! Alors ça c&#x27;est joli&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Ouah ! Alors ça c&#x27;est joli.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Ok !  On voit bien le module constant, et là on découvre que
l&#x27;argument est discrétisé, et ne peut prendre que 20 valeurs. Ça doit
être parce que la fréquence d&#x27;échantillonnage est un multiple de la
fréquence d&#x27;émission.  Bon, il est évident qu&#x27;il faut discrétiser ça :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;discrete = np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span&gt;(np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;round&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10 &lt;&#x2F;span&gt;&lt;span&gt;* np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;angle&lt;&#x2F;span&gt;&lt;span&gt;(iqs) &#x2F; np.pi + &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;), int)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(discrete[:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# [ 3  4  5  6  7  8  9 10 -9 -8 -7 -6 -5  6  7  8  9 10 -9 -8]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;C&#x27;est une suite qui a l&#x27;air d&#x27;être incrémentée de 1 en général,
et parfois de nombres plus grands.  Analysons donc les différences
successives.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Counter&lt;&#x2F;span&gt;&lt;span&gt;(discrete[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;:] - discrete[:-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Counter({1: 18228, -19: 1014, 11: 490, -9: 483})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Comme il semblait, la plupart du temps on a un incrément de 1.  De
temps en temps on a un saut de &lt;code&gt;-19 = 1 - 20&lt;&#x2F;code&gt; à cause du modulo, et
parfois on a des sauts de &lt;code&gt;11 = 1 + 10&lt;&#x2F;code&gt; ou &lt;code&gt;-9 = 1 - 10&lt;&#x2F;code&gt;, qui sont
visiblement des inversions de phase.  A priori, une différence de
&lt;code&gt;11&lt;&#x2F;code&gt; ou de &lt;code&gt;-9&lt;&#x2F;code&gt;, c&#x27;est la même chose modulo 20.  Donc reprenons ça
proprement :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;differences = (discrete[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;:] - discrete[:-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;] - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;) % &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;20
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(differences, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.-&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;show&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;figure&gt;
    &lt;img src=&quot;pap_plot_differences.svg&quot; alt=&quot;Et voilà le signal&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Et voilà le signal.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Bon, on a des périodes longues ou courtes en bas, et apparemment en
haut c&#x27;est toujours un seul point.  On peut vérifier avec un &lt;code&gt;groupby&lt;&#x2F;code&gt;
ici aussi :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;signals = [(value, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;list&lt;&#x2F;span&gt;&lt;span&gt;(group)))
&lt;&#x2F;span&gt;&lt;span&gt;           &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;value, group &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;it.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;groupby&lt;&#x2F;span&gt;&lt;span&gt;(differences)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Counter&lt;&#x2F;span&gt;&lt;span&gt;(signals))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Counter({(10, 1): 973, (0, 15): 682, (0, 31): 290, (0, 12): 1, (0, 10): 1})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;C&#x27;est correct.  On pourrait être tenté de décoder ça
avec un 0 pour les courtes (15) et un 1 pour les longues (31), ou
inversement, mais là il faut lire l&#x27;énoncé, qui donne un
indice : &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Manchester_code&quot;&gt;Manchester&lt;&#x2F;a&gt;.
On voit sur la page qu&#x27;en effet, un code Manchester est constitué de
périodes courtes (une unité), et des longues (deux unités).  On
dirait qu&#x27;on est sur la bonne voie !&lt;&#x2F;p&gt;
&lt;p&gt;En réfléchissant un peu avec la figure de Wikipédia, je me rends
compte qu&#x27;une période longue signifie qu&#x27;on change de symbole (0 à 1,
ou 1 à 0), et deux périodes courtes (qui viennent toujours par deux)
signifient qu&#x27;on ne change pas de symbole (si on était à 0, on reste
à 0, et pareil pour 1).  Avec cette technique, on ne sait pas de quel
symbole on démarre, mais ce n&#x27;est pas grave : il n&#x27;y en a que deux (0
et 1), on peut essayer les deux si besoin.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;data = [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;i = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span&gt;i &amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(signals):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;signals[i][&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;] &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        data.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;append&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span&gt;- data[-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;        i += &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        data.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;append&lt;&#x2F;span&gt;&lt;span&gt;(data[-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;        i += &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4
&lt;&#x2F;span&gt;&lt;span&gt;msg = &amp;#39;&amp;#39;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;join&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;(x) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;data)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(msg[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# 10110011100101100001011000100011001101100011001101
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On a la suite de bits, qu&#x27;il faut maintenant transformer en texte :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;s = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bytes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;(msg[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;*i:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;*i+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;range&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(msg)&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;text = s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;utf8&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;=&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ignore&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(s)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# ƞɝƞƙʙʛƙȝΜϙȚϞʛǂ
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Hmm.  Ça n&#x27;a pas l&#x27;air d&#x27;être ça.  Essayons d&#x27;inverser les 0 et les 1 :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;msg = &amp;#39;&amp;#39;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;join&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;-x) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;data)
&lt;&#x2F;span&gt;&lt;span&gt;s = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bytes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;(msg[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;*i:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;*i+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;range&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(msg)&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;text = s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;utf8&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;=&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ignore&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(s)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# FCSC{9ab3c6bc...}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Et voilà un deuxième flag !&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zodiaque&quot;&gt;Zodiaque&lt;&#x2F;h1&gt;
&lt;p&gt;C&#x27;était l&#x27;épreuve la plus difficile de cette catégorie.  Je n&#x27;ai pas
réussi à la terminer pendant la compétition, mais en lisant des
writeups après coup je me suis rendu compte que j&#x27;étais vraiment très
proche, et j&#x27;ai juste eu à changer un seul nombre dans mon code pour
obtenir le flag.  Même principe, on démarre encore avec un fichier
&lt;code&gt;challenge.iq&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;zodiaque_plot_complex.svg&quot; alt=&quot;L&#x27;argument semble intéressant&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;L&#x27;argument semble intéressant.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Alors là, les parties réelle et imaginaires ne sont d&#x27;aucun
secours, le module est un peu bizarre, mais l&#x27;argument est clairement
intéressant.  Il est discrétisé en semble-t-il quatre valeurs, qui
varient linéairement dans le temps.  Je vois une discrétisation dans
un signal analogique, je suis content parce que ça sent le signal
numérique.  Commençons par retirer cette dérive.  On dérive de
&lt;code&gt;2pi&lt;&#x2F;code&gt; en 1000 points de temps, donc on peut construire une fonction
linéaire qui a cette dérivée, modulo &lt;code&gt;2pi&lt;&#x2F;code&gt;, et la soustraire de
l&#x27;argument pour avoir des valeurs stables :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;refs = (np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;range&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(iqs)) * np.pi &#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;500&lt;&#x2F;span&gt;&lt;span&gt;) % (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;*np.pi) - np.pi
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;angle&lt;&#x2F;span&gt;&lt;span&gt;(iqs) - refs)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;show&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;figure&gt;
    &lt;img src=&quot;zodiaque_plot_argument_redresse.svg&quot; alt=&quot;On voit mieux les valeurs discrétisées&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;On voit mieux les valeurs discrétisées.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;C&#x27;est un peu moche parce qu&#x27;on a pris un modulo, mais c&#x27;est mieux pour
discrétiser.  En zoomant encore un peu, on se rend compte que c&#x27;est moins
discret que ça en a l&#x27;air, et il y a en fait des points partout.&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;zodiaque_plot_argument_redresse_zoom.svg&quot; alt=&quot;Pas si discret que ça&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Pas si discret que ça.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Il va falloir sous-échantillonner.  Les transitions brutales semblent
être des artefacts du modulo.  Les maximums et minimums &amp;quot;doux&amp;quot; semblent
plus fiables.  On compte les points entre deux maximums ou minimums : c&#x27;est
toujours un multiple de 8.  Ça semble donc bien de prendre un point sur 8.
Un peu de tâtonnement à la main plus tard pour trouver le bon offset :&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;zodiaque_plot_argument_redresse_zoom_subsampled.svg&quot; alt=&quot;Ça semble correct&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Ça semble correct.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;On dézoome un peu pour vérifier que ça marche toujours :&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;zodiaque_plot_argument_redresse_subsampled.svg&quot; alt=&quot;Ça commence à ressembler à quelque chose&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Ça commence à ressembler à quelque chose.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Donc reprenons proprement :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;subsample = (np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;angle&lt;&#x2F;span&gt;&lt;span&gt;(iqs) - refs)[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;discrete = np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span&gt;(np.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;round&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;* subsample &#x2F; np.pi), int) % &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(discrete[:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# [2 1 0 2 2 0 1 1 3 3 3 2 0 0 3 0 2 0 1 2]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Counter&lt;&#x2F;span&gt;&lt;span&gt;(discrete))
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Counter({0: 4159, 1: 3053, 2: 2911, 3: 1976})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Il y a un peu plus de 0 et un peu moins de 3 que le reste, mais ce n&#x27;est
pas déraisonnable.  J&#x27;ai envie de décoder ces quatre valeurs avec &lt;code&gt;00, 01, 10, 11&lt;&#x2F;code&gt; mais dans quel ordre ?  Il n&#x27;y a que 24 possibilités,
essayons-les toutes. &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#offset&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; On sait qu&#x27;il y a &lt;code&gt;FCSC&lt;&#x2F;code&gt; dans le texte
donc servons-nous en.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;offset&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;En fait, il faudrait aussi idéalement itérer sur l&#x27;offset
(4 valeurs possibles) parce qu&#x27;on ne sait pas si le premier symbole que
l&#x27;on reçoit est au début d&#x27;un octet ou au milieu.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;decode = [&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;00&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;01&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;11&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;perm &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;it.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;permutations&lt;&#x2F;span&gt;&lt;span&gt;(decode):
&lt;&#x2F;span&gt;&lt;span&gt;    msg = &amp;#39;&amp;#39;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;join&lt;&#x2F;span&gt;&lt;span&gt;(perm[x] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;discrete)
&lt;&#x2F;span&gt;&lt;span&gt;    s = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bytes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;(msg[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;*i:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;*i+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;range&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(msg)&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;    text = s.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;utf8&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;=&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ignore&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;text.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;find&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;FCSC&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;) != -&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(text)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Et voilà comment on obtient un troisième flag.  C&#x27;était l&#x27;étape
de sous-échantillonnage dont je n&#x27;avais pas réussi à trouver les
paramètres pendant le concours.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;coda&quot;&gt;Coda&lt;&#x2F;h1&gt;
&lt;p&gt;J&#x27;ai eu de la chance que 15 lignes de Python suffisent pour
ces épreuves cette année, mais pour en savoir plus je
recommande fortement la lecture de writeups plus sérieux
(&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;PVaub&#x2F;FCSC2021&quot;&gt;exemple&lt;&#x2F;a&gt;) où on apprend à faire
les choses correctement.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Graphology</title>
        <published>2021-03-08T00:00:00+00:00</published>
        <updated>2021-03-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://suboptimal.fr/2021-03-graphology/"/>
        <id>https://suboptimal.fr/2021-03-graphology/</id>
        
        <content type="html" xml:base="https://suboptimal.fr/2021-03-graphology/">&lt;p&gt;I thought about &amp;quot;how to draw a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Graph_(discrete_mathematics)&quot;&gt;graph&lt;&#x2F;a&gt;&amp;quot; quite a bit this last month, and I have &lt;em&gt;things to say&lt;&#x2F;em&gt; about it. They&#x27;re probably not that interesting though, so I&#x27;m going to say them in this forlorn blog.&lt;&#x2F;p&gt;
&lt;p&gt;Among stupid abstract concepts that mathematicians seem to love, graphs are among the most drawable. Which is unexpected, because their definition is completely disconnected from any form of spatial embedding: they don&#x27;t need a metric space, or a concept of distance. Yet it&#x27;s fairly obvious that &amp;quot;how a graph is drawn&amp;quot; (in two dimensions, on a sheet of paper) matters a lot. Take for example &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Mark_Lombardi&quot;&gt;Lombardi&#x27;s&lt;&#x2F;a&gt; artsy conspiracies. Would we care about the relations between the Bush and bin Laden families without these beautiful drawings?&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;lombardi-drawing.jpg&quot; alt=&quot;One of Lombardi conspirationist and wonderful graph layout, they are hard to find in high-resolution. The establishment, no doubt.&quot; width=&quot;60%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;One of Lombardi conspirationist and wonderful graph layout, they are hard to find in high-resolution. The establishment, no doubt..&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;So graphs are inextricably related to &lt;strong&gt;graph layouts&lt;&#x2F;strong&gt;. And finding the &amp;quot;right&amp;quot; graph layout with a computer (or without, for that matter) is a complicated problem. In part because &amp;quot;right&amp;quot; is hard to define (both aesthetically and practically), but even with a well-defined objective function, graphs tend to be large and optimization becomes complicated fast.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;force-directed-layouts&quot;&gt;Force-directed layouts&lt;&#x2F;h3&gt;
&lt;p&gt;Graph drawing has a rich history that I&#x27;m not going to explore. Focusing on modern algorithm, and ignoring trivial or specialised ones, most layouts are more or less involved versions of what&#x27;s called a &lt;em&gt;force-directed layout&lt;&#x2F;em&gt; (FDL).&lt;&#x2F;p&gt;
&lt;p&gt;A force-directed layout is a natural idea, for a physicist at least. Edges are replaced by some sort of attractive force (think springs) and nodes can repel each other. So, highly connected nodes cluster together, and isolated ones are pushed toward the periphery. For example, denoting $x_i$ the nodes positions, you&#x27;ll be trying to minimize something like:
$$
E = \sum_{\mathrm{i-j\ connected}} (x_i - x_j - l_{i, j})^2 - \sum_{i,\ j} \frac{e_{i, j}}{|x_i - x_j|}
$$&lt;&#x2F;p&gt;
&lt;p&gt;Obviously you could consider an infinity of small variations starting with this template:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Some kind of central gravity to avoid nodes flying far away&lt;&#x2F;li&gt;
&lt;li&gt;An &amp;quot;electric field&amp;quot; for directed graphs&lt;&#x2F;li&gt;
&lt;li&gt;Fixed nodes&lt;&#x2F;li&gt;
&lt;li&gt;Avoided crossings&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Ultimately by choosing custom potentials it&#x27;s easy to place every node exactly where you want them, which kind of defeats the point. What&#x27;s more, most of these options are not readily implemented (except if you like 40 years-old pseudo-code). &lt;a href=&quot;https:&#x2F;&#x2F;igraph.org&#x2F;python&#x2F;&quot;&gt;igraph&lt;&#x2F;a&gt; (the reference, at least in R&#x2F;python&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#python&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;), only implements standard algorithms: fruchterman-reingold (repelling force between nodes), kamada-kawai (springs alone, but with non-zero lengths at rest) and drl (a variation on fruchterman-reingold). The minimisation step is often done with some sort of annealing process which can become quite involved&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#drl&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;div class=&quot;apparte&quot;&gt;
    &lt;p&gt;On that note, Wikipedia criticizes the FDL layouts for rarely reaching their true optimum. I tend to disagree with Wikipedia here, metastates are a good thing in this type of layout: they allow you to nudge the graph in the right direction with smart initial conditions (something that&#x27;s used quite a bit in the drl algorithm for example). &lt;a href=&quot;https:&#x2F;&#x2F;tvtropes.org&#x2F;pmwiki&#x2F;pmwiki.php&#x2F;Main&#x2F;TitleDrop&quot;&gt;Suboptimal&lt;&#x2F;a&gt; can be good.&lt;&#x2F;p&gt;

&lt;&#x2F;div&gt;
&lt;p&gt;A typical example to test these different algorithms is the so-called &amp;quot;Swiss roll&amp;quot; graph. To build it, start with a $L \times N$ dots arranged on a two-dimensional rectangular grid, then roll this rectangle along one of its axis in three-dimension. The distances between the dots can then be used to make a complete graph with weighted edges.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#distances&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;!-- ```py --&gt;
&lt;!-- import sklearn.datasets --&gt;
&lt;!-- from itertools import product --&gt;
&lt;!-- import igraph as ig --&gt;
&lt;!-- X, _ = sklearn.datasets.make_swiss_roll(n_samples=2000, noise=0.01) --&gt;
&lt;!-- g = ig.Graph() --&gt;
&lt;!-- g.add_vertices(2000) --&gt;
&lt;!-- g.add_edges([(a, b) for a, b in product(range(n_samples), 2) if a != b]  --&gt;
&lt;!-- # define weights to be the inverse of the (euclidian) distance --&gt;
&lt;!-- g.es[&#x27;weight&#x27;] = [1&#x2F;np.sqrt(((X[a, :] - X[b, :])**2).sum())  --&gt;
&lt;!--                     for a, b in product(range(n_samples), 2) if a != b] --&gt;
&lt;!-- layout = g.layout_fruchterman_reingold(weights=&#x27;weight&#x27;, maxiter=500) --&gt;
&lt;!-- ``` --&gt;
&lt;figure class=&quot;multiple_img&quot;&gt;
&lt;img src=&quot;3d.png&quot; class=&quot;inline_img&quot; width=30% &#x2F;&gt; &lt;img src=&quot;fr_fdl.png&quot; class=&quot;inline_img&quot; width=30% &#x2F;&gt;
&lt;figcaption&gt;&lt;p&gt;&lt;em&gt; Projection of the 3-dimensional Swiss roll on the left and the result of the fruchterman-reingold algorithm on the right.  &lt;&#x2F;em&gt;&lt;&#x2F;p&gt;&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;As visible above, the FDL algorithm does a good job with this dataset, even if some of the clusters are not as separated as they could be, the structure is well conserved.&lt;&#x2F;p&gt;
&lt;p&gt;Even then, this example hints at another option, which is less used (and arguably less good in many cases) but, at least to me, feels more interesting conceptually. It consists in two stage:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;First, embedding the graph into a high dimensional space where its internal tensions are at least somewhat resolved&lt;&#x2F;li&gt;
&lt;li&gt;Then reduce the dimension back to two-dimensions, while keeping the structure of the high dimensional space.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;multidimensional-scaling&quot;&gt;Multidimensional scaling&lt;&#x2F;h3&gt;
&lt;p&gt;To resolve the tensions created by the edges in the high dimensional space, we would like to have the euclidean distances between pairs of nodes inversely proportional to the weight of the edge.space.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s start simple and assume that:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The graph is complete (all pair of nodes are linked)&lt;&#x2F;li&gt;
&lt;li&gt;The inverse of the weights between pairs of nodes are already equal to the euclidean distance between point in some high dimensional space of (unknown) dimension&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Assuming this to be true, then we just need to find back the nodes positions given the distances. This is a well-known problem, called multidimensional scaling. And in the case of euclidean distances it admits an &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Multidimensional_scaling#Metric_multidimensional_scaling_(mMDS)&quot;&gt;elegant solution&lt;&#x2F;a&gt;. This is well known, but I can&#x27;t resist re-derivating it here.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s call $D$ the matrix verifying $S_{i,j} = w_{i, j}^{-1}$. If $D$ represents euclidean distances, $D_{i, j}^2 = || \mathbf{x}_i - \mathbf{x}_j ||^2$ , then&lt;&#x2F;p&gt;
&lt;p&gt;$$S_{i, j} = D_{i, j} - D_{a, j} - D_{j, a} = 2(\mathbf{x}_i - \mathbf{x}_a)\cdot(\mathbf{x}_j - \mathbf{x}_a)$$&lt;&#x2F;p&gt;
&lt;p&gt;So $S = \mathbf{x}^T \mathbf{x}$, and $S$ is positive semi-definite&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#posdef&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. If the eigenvalues and eigenvectors of $S$ are defined as $\lambda_i$ and $\mathbf{u_i}$ respectively, then&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#sign&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;$$ \mathbf{x} = \sum_j \mathbf{u_j} \sqrt{\lambda_j} $$&lt;&#x2F;p&gt;
&lt;p&gt;So in practice, finding back the position of the dots is as easy as diagonalising the matrix $S$.&lt;&#x2F;p&gt;
&lt;p&gt;However, in most cases hypothesis 2 is not verified, i.e. if the matrix $D$ is not a distance matrix. The triangle inequality for example is unlikely to be respected for every graph. We can still try to apply the algorithm I sketched out above, in an arbitrary dimension, and hope for the best. $S$ is diagonalisable (because symmetric), so we can always find eigenvectors and eigenvalues. However, some of these eigenvalues are probably negative. What happens if we simply replace the potential negative eigenvalues by zero (without modifying the associated eigenspace), then, writing:&lt;&#x2F;p&gt;
&lt;p&gt;$$ \mathbf{x} = \sum_{j | \lambda_j &amp;gt; 0} \mathbf{u_j} \sqrt{\lambda_j} $$&lt;&#x2F;p&gt;
&lt;p&gt;The matrix &lt;strong&gt;$\mathbf{x} \mathbf{x}^T$ is then the closest positive definite matrix to $S$&lt;&#x2F;strong&gt;, in terms of the Frobenius norm&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#src&quot;&gt;6&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Explicitly, this algorithm gives the solution to the following minimisation problem (for an arbitrary reference point $a$)&lt;&#x2F;p&gt;
&lt;p&gt;$$
\sum_{\mathrm{i, j}} (d_{i, j}^2 - d_{a, i}^2 - d_{a, j}^2 - (x_i - x_a)(x_j - x_a))^2
$$&lt;&#x2F;p&gt;
&lt;p&gt;Assuming $S$ is close to semi-definite&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#ep&quot;&gt;7&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; this becomes a spring model, with spring resting length equal to $d_{i, j}$. Not so far from an FDL layout, in the end.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dimensionality-reduction&quot;&gt;Dimensionality reduction&lt;&#x2F;h3&gt;
&lt;p&gt;Now we have a potentially very good graph representation, but it&#x27;s in an arbitrary dimension. The return to the two-dimensional world can usually be done with one of the standard dimensionality reduction algorithms. PCA is the traditional one&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#pca&quot;&gt;8&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;T-distributed_stochastic_neighbor_embedding&quot;&gt;t-SNE&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;umap-learn.readthedocs.io&#x2F;en&#x2F;latest&#x2F;&quot;&gt;UMAP&lt;&#x2F;a&gt; are the cool and trendy ones.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s test this. With the Swiss roll&#x27;s example, this method works very well (obviously, it was made for it):&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;gralt_swiss_roll.png&quot; alt=&quot;Multidimensional scaling followed by PCA, applied to the Swiss roll graph&quot; width=&quot;40%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Multidimensional scaling followed by PCA, applied to the Swiss roll graph.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;But how does it fare with a real-life example, where the edges weights may not represent actual distances?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;butterflies&quot;&gt;Butterflies&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s play with a &lt;a href=&quot;https:&#x2F;&#x2F;snap.stanford.edu&#x2F;biodata&#x2F;datasets&#x2F;10029&#x2F;10029-SS-Butterfly.html&quot;&gt;cute dataset&lt;&#x2F;a&gt;. In this graph, nodes represent photos of butterflies and edges the visual similarities between them. There are different (sub?)-species of butterflies that we&#x27;re going to use as ground-truth labels.&lt;&#x2F;p&gt;
&lt;p&gt;In that case the graph is not complete (hypothesis 1 is not verified), so a preprocessing step is needed. Because the graph is connected, it can be made complete by measuring the shortest (weighted) distances between unlinked points (using Dijkstra algorithm typically).&lt;&#x2F;p&gt;
&lt;p&gt;The results are shown below. The MDS-projection via PCA does badly, but the MDS-UMAP one performs very well, and the result is qualitatively better than the layout-based on a force-directed algorithm.&lt;&#x2F;p&gt;
&lt;figure class=&quot;multiple_img&quot;&gt;
&lt;img src=&quot;butterfly_fdl_fr.png&quot; class=&quot;inline_img&quot; width=30% &#x2F;&gt; &lt;img src=&quot;butterfly_pca.png&quot; class=&quot;inline_img&quot; width=30% &#x2F;&gt;  &lt;img src=&quot;butterfly_umap.png&quot; class=&quot;inline_img&quot; width=30% &#x2F;&gt;
&lt;figcaption&gt;&lt;p&gt;&lt;em&gt; From left to right Fruchterman-Reingold algorithm, PCA based MDS-projection algorithm, and UMAP-based MDS-projection algorithm.  &lt;&#x2F;em&gt;&lt;&#x2F;p&gt;&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;It&#x27;s &lt;a href=&quot;#&quot;&gt;packaged&lt;&#x2F;a&gt;. This is not a very original idea –while researching it I found one paper that implemented MDS-PCA– and the fact that it&#x27;s not used much is probably proof enough that in general it&#x27;s not that great.
So I don&#x27;t encourage you to use it before the tried-and-true force-directed-layout methods. Standards exist for a reason. But if these methods don&#x27;t work for you, well, why not. It&#x27;s a relatively fast method (but a larger dataset may kill your memory) and the high-dimensional space can be useful in itself.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;python&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;Following the time-honored tradition of &amp;quot;if it isn&#x27;t implemented in my favourite language it may as well not exist&amp;quot;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;drl&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;drl, or &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;decitre&#x2F;openord&quot;&gt;Openord&lt;&#x2F;a&gt; as it is called now, is particularly complicated but works impressively well. To my knowledge nobody tried to recode it from scratch... Probably for good reasons.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;distances&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;Hereafter, I&#x27;ll take the convention that weights represent the inverse of a distance. This is of course arbitrary.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;posdef&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;The reverse is also true, if a matrix is positive definite it admits a Gram representation: there&#x27;s some dimension in which it can be represented as a scalar product of vectors.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;sign&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;5&lt;&#x2F;sup&gt;
&lt;p&gt;$\lambda_i$ is positive, due to the positive-definiteness of $S$.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;src&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;6&lt;&#x2F;sup&gt;
&lt;p&gt;Cheng and Higham, 1998 for a more general result. Also &lt;a href=&quot;https:&#x2F;&#x2F;nhigham.com&#x2F;2021&#x2F;01&#x2F;26&#x2F;what-is-the-nearest-positive-semidefinite-matrix&#x2F;&quot;&gt;this blog post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;ep&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;7&lt;&#x2F;sup&gt;
&lt;p&gt;All its negative eigenvalues are close to $0$.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Guessing lottery game</title>
        <published>2019-05-15T00:00:00+00:00</published>
        <updated>2019-05-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://suboptimal.fr/2019-05-guessing-game/"/>
        <id>https://suboptimal.fr/2019-05-guessing-game/</id>
        
        <content type="html" xml:base="https://suboptimal.fr/2019-05-guessing-game/">&lt;p&gt;I found a fun problem &lt;a href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;dataisbeautiful&#x2F;comments&#x2F;acow6y&#x2F;asking_over_8500_students_to_pick_a_random_number&#x2F;eda56jd&quot;&gt;on
reddit&lt;&#x2F;a&gt; some time ago:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A number is randomly selected between two bounds.  Each player can
make one guess, the closest to the selected number wins.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The author of this comment notes that they &amp;quot;always use a random number
generator for [their] pick rather than guessing to avoid accidentally
clustering with others&amp;quot;.  This relies on the idea illustrated by this
reddit thread, that people tend to favour the same numbers when asked to
make a random choice, thus generating non-uniform distributions that can
create lumps.  Picking a number from a uniform distribution might then
increase the chances to pick it in a less preferred region, and thus, the
chances to be the closest from the target.&lt;&#x2F;p&gt;
&lt;p&gt;But could we do better than that?  What is the optimal strategy?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;against-unbiased-opponents&quot;&gt;Against unbiased opponents&lt;&#x2F;h1&gt;
&lt;p&gt;We could start assuming that the opponents are unbiased and all pick
numbers from the uniform distribution on the allowed range.  Knowing
that, where should we play?&lt;&#x2F;p&gt;
&lt;p&gt;First of all, we should decide on a couple of details on the lottery
that we will be modelling: discrete (we can pick any integer
from &lt;script type=&quot;math&#x2F;tex&quot;&gt;1&lt;&#x2F;script&gt;
 to &lt;script type=&quot;math&#x2F;tex&quot;&gt;N&lt;&#x2F;script&gt;
 included), or continuous
(we can pick any real number between &lt;script type=&quot;math&#x2F;tex&quot;&gt;0&lt;&#x2F;script&gt;

and &lt;script type=&quot;math&#x2F;tex&quot;&gt;1&lt;&#x2F;script&gt;
), and what should we do in case of tie.  These
rules lead to slightly different behaviours, but I suggest we study
both the discrete and continuous cases.  Ties will not happen in the
continuous case, but in the discrete case, we could either&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;give a whole prize to everyone tied first;&lt;&#x2F;li&gt;
&lt;li&gt;make another lottery among only the people tied first, and repeatedly until
only one person remains;&lt;&#x2F;li&gt;
&lt;li&gt;split the prize equally between the people tied first;&lt;&#x2F;li&gt;
&lt;li&gt;decide that everyone lost.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The second case is a bit more delicate to study because it would be
reasonable to allow a change of strategy between the rounds, depending
on the number of opponents, so I propose that we restrain ourselves to
the simple cases where ties cause the prize to be either multiplied,
split, or destroyed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;discrete-case&quot;&gt;Discrete case&lt;&#x2F;h2&gt;
&lt;p&gt;We are searching for the winning expectancy &lt;script type=&quot;math&#x2F;tex&quot;&gt;\mathbf{E}(N,K,C)&lt;&#x2F;script&gt;

against &lt;script type=&quot;math&#x2F;tex&quot;&gt;K&lt;&#x2F;script&gt;
 opponents, if we play at &lt;script type=&quot;math&#x2F;tex&quot;&gt;C&lt;&#x2F;script&gt;

(between &lt;script type=&quot;math&#x2F;tex&quot;&gt;1&lt;&#x2F;script&gt;
 and &lt;script type=&quot;math&#x2F;tex&quot;&gt;N&lt;&#x2F;script&gt;
).&lt;&#x2F;p&gt;
&lt;p&gt;Assume the selected number is &lt;script type=&quot;math&#x2F;tex&quot;&gt;S&lt;&#x2F;script&gt;
.  Then we can count
the number &lt;script type=&quot;math&#x2F;tex&quot;&gt;N_=&lt;&#x2F;script&gt;
 of numbers &lt;script type=&quot;math&#x2F;tex&quot;&gt;X&lt;&#x2F;script&gt;
 at the
same distance from the target as we are: such that &lt;script type=&quot;math&#x2F;tex&quot;&gt;|X-S|
= |C-S|&lt;&#x2F;script&gt;
.  This will be &lt;script type=&quot;math&#x2F;tex&quot;&gt;1&lt;&#x2F;script&gt;
 or &lt;script type=&quot;math&#x2F;tex&quot;&gt;2&lt;&#x2F;script&gt;
.
We can also count the number &lt;script type=&quot;math&#x2F;tex&quot;&gt;N_\gt&lt;&#x2F;script&gt;
 of
numbers &lt;script type=&quot;math&#x2F;tex&quot;&gt;X&lt;&#x2F;script&gt;
 farther from the target than we are: such
that &lt;script type=&quot;math&#x2F;tex&quot;&gt;|X-S| \gt |C-S|&lt;&#x2F;script&gt;
. If &lt;script type=&quot;math&#x2F;tex&quot;&gt;E&lt;&#x2F;script&gt;
 of our
opponents are at the same distance from the target as we are, and all
the others are farther, then in case we split the prize, we
win &lt;script type=&quot;math&#x2F;tex&quot;&gt;\frac{1}{E+1}&lt;&#x2F;script&gt;
 of the prize.  This happens with
probability &lt;script type=&quot;math&#x2F;tex&quot;&gt;\binom{K}{E} \left(\frac{N_=}{N}\right)^E
\left(\frac{N_\gt}{N}\right)^{K-E}&lt;&#x2F;script&gt;
 for given &lt;script type=&quot;math&#x2F;tex&quot;&gt;C&lt;&#x2F;script&gt;

and &lt;script type=&quot;math&#x2F;tex&quot;&gt;S&lt;&#x2F;script&gt;
.  The total winning expectancy is then&lt;&#x2F;p&gt;
&lt;script type=&quot;math&#x2F;tex;mode=display&quot;&gt;\mathbf{E}(N,K,C) = \frac{1}{N}\sum_{S=1}^{N}{
    \sum_{E=0}^{K}{
    \frac{\binom{K}{E} \left(\frac{N_=(C,S)}{N}\right)^E
    \left(\frac{N_&gt;(C,S)}{N}\right)^{K-E}}{E+1}
    }
}&lt;&#x2F;script&gt;
&lt;figure&gt;
    &lt;img src=&quot;lottery_fdiscrete1.svg&quot; alt=&quot;Expectancy to win in the uniform discrete case if the prize is split between the exaequos, for N=10&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Expectancy to win in the uniform discrete case if the prize is split between the exaequos, for N=10.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;From this graph, we indeed win every time when we have no opponent, we
win less often as the number of opponents increases, and picking the
middle region seems in general to be a good choice.&lt;&#x2F;p&gt;
&lt;p&gt;But as the number of opponents goes up, the winning expectancy goes down
and it becomes more difficult to distinguish the strategy.  To make
it appear more clearly, I will normalize each curve such that their
integrals are equal (more precisely I divide them by the total winning
expectancy for this number of opponents).&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;lottery_fdiscrete2.svg&quot; alt=&quot;Strategies for the uniform discrete case if the prize is split between the exaequos, for N=10&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Strategies for the uniform discrete case if the prize is split between the exaequos, for N=10.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;What jumps to the eye is that for enough opponents, the best strategy
is not to play in the middle any more.  We can also note that the
strategies against 1 or 2 opponents are identical.  Finally, when the
number of opponents becomes very large, the best strategy becomes to
chose uniformly: indeed in this case all the numbers will probably have
been chosen by several people, so that we only win if we pick exactly
the selected number.&lt;&#x2F;p&gt;
&lt;p&gt;Similar reasoning allows to obtain formulas for the other rules:&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;lottery_uniform.svg&quot; alt=&quot;Strategies for the uniform discrete case, for N=10&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Strategies for the uniform discrete case, for N=10.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;continuous-case&quot;&gt;Continuous case&lt;&#x2F;h2&gt;
&lt;p&gt;A closed-form formula is easier to find for the continuous case:
following a similar reasoning, the winning expectancy if we play
at &lt;script type=&quot;math&#x2F;tex&quot;&gt;x\in[0,1]&lt;&#x2F;script&gt;
 against &lt;script type=&quot;math&#x2F;tex&quot;&gt;K&lt;&#x2F;script&gt;
 opponents is&lt;&#x2F;p&gt;
&lt;script type=&quot;math&#x2F;tex;mode=display&quot;&gt;\mathbf{E}(K, x) = \frac{2+x^K(1+K-x\,(2+K))+(1-x)^K(x\,(2+K)-1)}{2\,(1+K)}&lt;&#x2F;script&gt;
&lt;figure&gt;
    &lt;img src=&quot;lottery_fcontinuous1.svg&quot; alt=&quot;Expectancy to win in the continuous case&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Expectancy to win in the continuous case.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;figure&gt;
    &lt;img src=&quot;lottery_fcontinuous2.svg&quot; alt=&quot;Strategies in the continuous case&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Strategies in the continuous case.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;A small advantage seems to subsist in the continuous case,
since we can make a guess as close to the border as we
want.  The positions of the two peaks of the
strategy &lt;script type=&quot;math&#x2F;tex&quot;&gt;p(K)&lt;&#x2F;script&gt;
 and &lt;script type=&quot;math&#x2F;tex&quot;&gt;1-p(K)&lt;&#x2F;script&gt;
 are such
that &lt;script type=&quot;math&#x2F;tex&quot;&gt;p(K)\sim\frac{2}{2+K}&lt;&#x2F;script&gt;
 when &lt;script type=&quot;math&#x2F;tex&quot;&gt;K&lt;&#x2F;script&gt;

tends to infinity, and &lt;script type=&quot;math&#x2F;tex&quot;&gt;(1+K)\\,\mathbf{E}(K,p(K)) =
(1+K)\\,\mathbf{E}(K,1-p(K)) \to 1+\frac{1}{2\\,\mathrm{e}^2} \approx
1.068&lt;&#x2F;script&gt;
, the demonstration of these properties is left as an exercise
to the reader.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;back-to-the-real-world&quot;&gt;Back to the real world&lt;&#x2F;h1&gt;
&lt;p&gt;Ok but the point of the reddit post was that humans don&#x27;t pick numbers
uniformly.  What does everything become if the selected number is still
uniformly random, but the opponents pick at random according to the
distribution given in the post (favouring 7 and choosing infrequently 10
or 1)?&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;lottery_realworld.svg&quot; alt=&quot;Strategies for the real world, for N=10&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Strategies for the real world, for N=10.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Depending on the rules of the lottery, it is either beneficial to pick the 7,
or to avoid the 7.  Until at some point, the less preferred numbers 10 and 1
become the best options, at least in the versions that encourage winning alone
(all ties lose, and to a lesser extent all ties split).&lt;&#x2F;p&gt;
&lt;p&gt;But as nice as all this seems, I wouldn&#x27;t encourage betting your house
with this strategy.  Indeed, it assumes random opponents and is not
protected against smart opponents which might observe you play a couple
of times and adapt their strategy accordingly.  To handle this kind
of adversaries, we can&#x27;t play the same number every time: a constant
strategy would be too easy to exploit.  We then need a mixed strategy,
which is a probability distribution over our possible actions.  This
will be for another post.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Stockholm visit guide</title>
        <published>2019-05-06T00:00:00+00:00</published>
        <updated>2019-05-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://suboptimal.fr/2019-05-stockholm/"/>
        <id>https://suboptimal.fr/2019-05-stockholm/</id>
        
        <content type="html" xml:base="https://suboptimal.fr/2019-05-stockholm/">&lt;p&gt;I spent two weeks in Stockholm, one of them dedicated to visiting the
city.  Here are some notes that could be useful for travelers.  It is
not at all exhaustive though, but just the result of my tastes and
peregrinations.  I would appreciate to know if you think I missed
interesting places!&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;vasa_kyrkan.jpg&quot; alt=&quot;The Gustav Vasa church in Vasastan&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;The Gustav Vasa church in Vasastan.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;h1 id=&quot;if-you-are-in-a-hurry-my-top-3&quot;&gt;If you are in a hurry, my top 3&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Östermalms saluhall&lt;&#x2F;em&gt;;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Nordiska museet&lt;&#x2F;em&gt; after 13 o&#x27;clock;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Historiska museet&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;general-tips&quot;&gt;General tips&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;1 EUR &lt;script type=&quot;math&#x2F;tex&quot;&gt;\approx&lt;&#x2F;script&gt;
 10 SEK, local currency can be withdrawn
at the airport (and at least with my bank, it is cheaper to withdraw
directly in SEK rather than accept the offered conversion in EUR, which
includes a significant change tax);&lt;&#x2F;li&gt;
&lt;li&gt;&amp;quot;Hello&amp;quot; = &amp;quot;Hej&amp;quot; or &amp;quot;Hejhej&amp;quot;, pronounced not too far from the american
&amp;quot;Hey&amp;quot;; &amp;quot;Thank you&amp;quot; = &amp;quot;Tack&amp;quot;, but almost everyone is willing and able to
help in English anyway;&lt;&#x2F;li&gt;
&lt;li&gt;Museums have a tendency to open late (10-11 AM) and close early (4-5 PM);&lt;&#x2F;li&gt;
&lt;li&gt;Single metro tickets bought at machines in metro stations are very
expensive (45 SEK).  If you buy one anyway, you obtain a paper ticket
that you are supposed to show to the clerk to enter the metro. It is
cheaper to buy a rechargable card for 20 SEK (lasts for life) at the
counter and charge it with trips.&lt;&#x2F;li&gt;
&lt;li&gt;Some metro stations are really pretty. But in general I prefer walking,
which is especially suitable in Stockholm since the city is not too
large.  Walking is also the only way to stumble on random places that
one wouldn&#x27;t have discovered otherwise.  Be aware though that Stockholm
is a relatively hilly city and some of the paved streets in the old city
are in a rather poor condition, so good shoes and a good physical condition
are appropriate.&lt;&#x2F;li&gt;
&lt;li&gt;Stockholm is spread over a handful of islands of different sizes and
ambiances, whose main ones are:
&lt;ul&gt;
&lt;li&gt;the continent,
&lt;ul&gt;
&lt;li&gt;Kungsholmen (&amp;quot;The King&#x27;s islet&amp;quot;), where my hostel was, but not
the island that I found the most interesting,&lt;&#x2F;li&gt;
&lt;li&gt;Gamla Stan (the old city) with the King&#x27;s palace, a lot of old
paved streets, tiny passages and many tourists.  Consequently, it
is not the place where the food is the cheapest.
&lt;ul&gt;
&lt;li&gt;Riddarholmen (&amp;quot;The Knights&#x27; islet&amp;quot;), a small island with
mostly administrative buildings and courthouses, but it is
prettier than it sounds,&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Skeppsholmen (&amp;quot;The islet of the ships&amp;quot;),&lt;&#x2F;li&gt;
&lt;li&gt;Djurgården (&amp;quot;The animal park&amp;quot;), also nicknamed &amp;quot;museum island&amp;quot;,&lt;&#x2F;li&gt;
&lt;li&gt;Södermalm.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;points-of-interest&quot;&gt;Points of interest&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;continent&quot;&gt;Continent&lt;&#x2F;h2&gt;
&lt;figure&gt;
    &lt;img src=&quot;observatory.jpg&quot; alt=&quot;Stockholm&#x27;s 18th century observatory&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Stockholm&#x27;s 18th century observatory.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=18&#x2F;59.33458&#x2F;18.09006&quot;&gt;Historiska museet&lt;&#x2F;a&gt;
Museum of the history of Sweden, free and interesting;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=19&#x2F;59.33594&#x2F;18.07908&quot;&gt;Östermalms saluhall&lt;&#x2F;a&gt;
Covered market, closed on Sundays.  It is a &lt;em&gt;very&lt;&#x2F;em&gt; nice place to buy
all kinds of fancy food (fish, meat, bread), and also to eat traditional
Swedish meals that can be heated up and served on the spot.  It is in a
temporary location until winter 2020, while the historical building is
being renovated.  Without contest one of my very favourite places in
Stockholm;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=17&#x2F;59.34182&#x2F;18.05457&quot;&gt;The observatory
hill&lt;&#x2F;a&gt; Nice
point of view to see the sunrise over Stockholm;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=19&#x2F;59.34792&#x2F;18.07239&quot;&gt;KTH
library&lt;&#x2F;a&gt; seems
open to everyone, is a pretty nice building and is a nice place to
study, or prepare the rest of the day;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=19&#x2F;59.35008&#x2F;18.06806&quot;&gt;KTH R1&lt;&#x2F;a&gt; The
dismantled (and safe) remains of the first nuclear reactor in Sweden.
It is now used as a culture center and exhibitions and parties are
regularly organized there.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;kungsholmen&quot;&gt;Kungsholmen&lt;&#x2F;h2&gt;
&lt;figure&gt;
    &lt;img src=&quot;city_hall.jpg&quot; alt=&quot;Stockholm&#x27;s city hall&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Stockholm&#x27;s city hall.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=18&#x2F;59.32742&#x2F;18.05477&quot;&gt;The city
hall&lt;&#x2F;a&gt; is open
to visits during the summer, but the view from under the archs is very
pretty all year long.  This is where the Nobel gala dinner is served
on December 10th.  Fun fact: the restaurant is open all year long and
can serve the menu from any given year&#x27;s gala, on the porcelain actually
used for the banquet.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;gamla-stan&quot;&gt;Gamla Stan&lt;&#x2F;h2&gt;
&lt;figure&gt;
    &lt;img src=&quot;riksdag.jpg&quot; alt=&quot;The Swedish parliament, not exactly on Gamla Stan but close enough&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;The Swedish parliament, not exactly on Gamla Stan but close enough.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Not my favourite part of the city: too many tourists.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=19&#x2F;59.32677&#x2F;18.07168&quot;&gt;The royal
palace&lt;&#x2F;a&gt;, the
official residence of the royal family (but they don&#x27;t usually live
here) can be visited.  As for the city hall, it is a massive building
but finely decorated inside.  Watching the changing of the guard in the
palace&#x27;s courtyard is a fun attraction;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=19&#x2F;59.32527&#x2F;18.07066&quot;&gt;The Nobel
museum&lt;&#x2F;a&gt; was a
little bit underwhelming but I probably had too high expectations.  It
is rather small, presents two showcases about Alfred Nobel, a temporary
exhibition and a permanent collection of some of the personal items that
the Nobel laureates are invited to donate to the museum.  Among them,
the scarf that Malala Yousafzai wore during her speech at UN, Geim and
Novoselov&#x27;s original scotch tape dispenser, or the sample jar Barry
Marshall drank his &lt;em&gt;Helicobacter pylori&lt;&#x2F;em&gt; broth from, but also a trumpet,
a calculator, an abacus belonging to the laureates… Most notably,
some of the prize diplomas were also exposed.  Each of them is unique,
and are expectedly some of the finest works of art there are, drawn and
colored by hand by Swedish artists.  They also have a video room with
short introductions to the work of the laureates playing in random
order: I could have spent hours in this room…&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;riddarholmen&quot;&gt;Riddarholmen&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=19&#x2F;59.32466&#x2F;18.06461&quot;&gt;Riddarholmskyrkan&lt;&#x2F;a&gt;
The visit costs a couple dozens of crowns, but I recommend it.  This
church is the sepulchre of the royal family.  It is better to see it
after the visit of &lt;em&gt;Historiska museet&lt;&#x2F;em&gt; to have some of the genealogy in
mind (it doesn&#x27;t help that everyone is named Gustav).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;skeppsholmen&quot;&gt;Skeppsholmen&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=19&#x2F;59.32738&#x2F;18.08182&quot;&gt;The museum of east-asian
art&lt;&#x2F;a&gt;: free and
open late (20 o&#x27;clock).  I have seen a nice exhibition on traditional
paper making in asian countries, and the Japan section of the museum
appealed to me.  The museum also hosts an extensive collection of
ancient chinese books (not that I could read them);&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=19&#x2F;59.32631&#x2F;18.08400&quot;&gt;Moderna museet&lt;&#x2F;a&gt;
that I didn&#x27;t have time to visit.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;djurgarden&quot;&gt;Djurgården&lt;&#x2F;h2&gt;
&lt;figure&gt;
    &lt;img src=&quot;nordiska.jpg&quot; alt=&quot;Nordiska museet, the museum of Nordic life&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Nordiska museet, the museum of Nordic life.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=18&#x2F;59.32912&#x2F;18.09367&quot;&gt;Nordiska
museet&lt;&#x2F;a&gt;: go in the
afternoon, it is free after 13 o&#x27;clock.  This is the museum of nordic life
and exposes the daily life of Swedes during the last 6 centuries.  The
building itself looks like a fairy tale castle.  Really worth the visit;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=18&#x2F;59.32808&#x2F;18.09126&quot;&gt;Vasamuseet&lt;&#x2F;a&gt;
exposes the local Titanic: a battle ship that sunk in 1628 in her maiden
trip and was resurfaced in 1961;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure&gt;
    &lt;img src=&quot;sami_storehouse.jpg&quot; alt=&quot;A traditional sami storehouse, elevated to protect the food from animals and snow&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;A traditional sami storehouse, elevated to protect the food from animals and snow.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=16&#x2F;59.3265&#x2F;18.1043&quot;&gt;Skansen&lt;&#x2F;a&gt; is an
old and large open air museum &#x2F; ecomuseum &#x2F; zoo.  The public is mostly
Swedish: families, or school classes.  I understood that Stockholmers
generally buy a ticket allowing entry all year long, and come back
regularly for the animations proposed all along the year around the
traditional holidays.  I visited over Easter and have seen for example
a Swedish grandma prepare traditional Easter cakes.  Furthermore, the
shop is nice, and there is a couple of restaurants and hot dog stands
inside: I would recommend to visit Skansen in the morning, eat there
and wait for 13 o&#x27;clock to go visit &lt;em&gt;Nordiska museet&lt;&#x2F;em&gt; just in front;&lt;&#x2F;li&gt;
&lt;li&gt;The botanical garden was unfortunately closed for restauration.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;sodermalm&quot;&gt;Södermalm&lt;&#x2F;h2&gt;
&lt;figure&gt;
    &lt;img src=&quot;stockholm_pano_cropped.png&quot; alt=&quot;View of Kungsholmen from Södermalm&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;View of Kungsholmen from Södermalm.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=17&#x2F;59.31999&#x2F;18.04984&quot;&gt;This part of the
island&lt;&#x2F;a&gt;,
besides being a nice picnic place, has very old traditional wooden
houses once occupied by the working class.  The view from this bank to
the north side of the city is alone worth the trip;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=19&#x2F;59.31961&#x2F;18.07116&quot;&gt;Stockholm&#x27;s city
museum&lt;&#x2F;a&gt;,
closed for restauration until a couple of days after my return date…&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;further&quot;&gt;Further&lt;&#x2F;h2&gt;
&lt;figure&gt;
    &lt;img src=&quot;vintervik.jpg&quot; alt=&quot;Lindholmen from Vinterviken&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Lindholmen from Vinterviken.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;ul&gt;
&lt;li&gt;The furthest I went by foot is
&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=16&#x2F;59.3113&#x2F;17.9859&quot;&gt;Vinterviken&lt;&#x2F;a&gt;, a
very bucolic place once owned by Nobel where he installed his dynamite
factory.  The beauty of the landscape contrasts with the rocks torn by the
experiments.  The place seems to be appreciated by Stockholmers since I
saw many of them jogging in the park, or going out with a baby stroller.
The old acid factory was converted into an exhibition center, and a
really nice restaurant.  You can take a tray, order one of the three
meals of the day, grab bread and water and go eat it outside at one
of the picnic tables, as a reward for the walk to arrive here from
Stockholm.  I warmly recommend it.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;even-further&quot;&gt;Even further&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;I wish I had the time to go pick a rock at
&lt;a href=&quot;https:&#x2F;&#x2F;www.openstreetmap.org&#x2F;#map=18&#x2F;59.42643&#x2F;18.35363&quot;&gt;Ytterby&lt;&#x2F;a&gt;&#x27;s
ytterbium quarry but that will be for another time.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure&gt;
    &lt;img src=&quot;cherrytree.jpg&quot; alt=&quot;One of the cherry trees of Kungsträdgården, a square that I discovered completely randomly on my last day&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;One of the cherry trees of Kungsträdgården, a square that I discovered completely randomly on my last day.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The perfect speed bump</title>
        <published>2018-10-20T00:00:00+00:00</published>
        <updated>2018-10-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://suboptimal.fr/2018-10-perfect-speed-bump/"/>
        <id>https://suboptimal.fr/2018-10-perfect-speed-bump/</id>
        
        <content type="html" xml:base="https://suboptimal.fr/2018-10-perfect-speed-bump/">&lt;p&gt;As he regularly does, Jason Cole recently delighted
the readers of his blog with &lt;a href=&quot;https:&#x2F;&#x2F;jasmcole.com&#x2F;2018&#x2F;10&#x2F;07&#x2F;the-perfect-speed-bump&#x2F;&quot;&gt;an interesting
problem&lt;&#x2F;a&gt;
that I will quickly sum up.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-problem&quot;&gt;The problem&lt;&#x2F;h1&gt;
&lt;blockquote&gt;
&lt;p&gt;You have been assigned the task to use a given amount of
concrete &lt;script type=&quot;math&#x2F;tex&quot;&gt;A&lt;&#x2F;script&gt;
 to build a speed bump over a section of a
road of length &lt;script type=&quot;math&#x2F;tex&quot;&gt;2\\,\ell&lt;&#x2F;script&gt;
.  But being a regular user of
the road, you are trying to do your job while minimizing the jolt felt
by a driver passing above it.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;We will consider the road as a 2D curve and the car as a
point-like particle on this curve.  We are then searching for a
function &lt;script type=&quot;math&#x2F;tex&quot;&gt;x \mapsto y(x)&lt;&#x2F;script&gt;
, with domain and
values &lt;script type=&quot;math&#x2F;tex&quot;&gt;[-\ell,\ell] \to \mathbb{R}&lt;&#x2F;script&gt;
.  We will assume
that the road outside of this interval lies at &lt;script type=&quot;math&#x2F;tex&quot;&gt;y=0&lt;&#x2F;script&gt;
;
the continuity conditions are then &lt;script type=&quot;math&#x2F;tex&quot;&gt;y(-\ell) = y(\ell) =
0&lt;&#x2F;script&gt;
, and we will also impose the nullity of the first derivatives
of &lt;script type=&quot;math&#x2F;tex&quot;&gt;y&lt;&#x2F;script&gt;
 at these two points, which we will note with a
parenthesized subscript &lt;script type=&quot;math&#x2F;tex&quot;&gt;y_{(x)}(-\ell) = y_{(x)}(\ell) = 0&lt;&#x2F;script&gt;
.&lt;&#x2F;p&gt;
&lt;p&gt;The author chose to minimize the integral of the square of the
vertical acceleration felt by the car passing over the
bump, &lt;script type=&quot;math&#x2F;tex&quot;&gt;\int_0^T{a_y(t)^2\mathrm{d}t}&lt;&#x2F;script&gt;
.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;analytical-results&quot;&gt;Analytical results&lt;&#x2F;h1&gt;
&lt;p&gt;Going quickly over the details because they already are in the original
post, conservation of energy and geometrical considerations give&lt;&#x2F;p&gt;
&lt;script type=&quot;math&#x2F;tex;mode=display&quot;&gt;\vec{v}(x) =
\sqrt{\frac{{v_0}^2-2\,g\,y}{1+{y_{(x)}}^2}}
\left(\vec{x}+y_{(x)}\,\vec{y}\right)&lt;&#x2F;script&gt;
&lt;p&gt;which, derived with respect to time, yields&lt;&#x2F;p&gt;
&lt;script type=&quot;math&#x2F;tex;mode=display&quot;&gt;\vec{a}(x) =
\frac{{v_0}^2-2\,g\,y}{1+{y_{(x)}}^2}\left[
-\left(\frac{g\,y_{(x)}}{{v_0}^2-2\,g\,y}+\frac{y_{(x)}\,y_{(x,x)}}{1+{y_{(x)}}^2}\right)\vec{x}
+\left(\frac{y_{(x,x)}}{1+{y_{(x)}}^2}-\frac{g\,{y_{(x)}}^2}{{v_0}^2-2\,g\,y}\right)\vec{y}
\right]&lt;&#x2F;script&gt;
&lt;p&gt;The quantity that we want to minimize is then&lt;&#x2F;p&gt;
&lt;script type=&quot;math&#x2F;tex;mode=display&quot;&gt;\begin{aligned}
\int_0^T{{a_y}^2\mathrm{d}t} &amp;= \int_{-\ell}^{\ell}{\frac{{a_y}^2}{v_x}\mathrm{d}x}\\
&amp;=
\int_{-\ell}^{\ell}{\left(\frac{{v_0}^2-2\,g\,y}{1+{y_{(x)}}^2}\right)^{3&#x2F;2}
\left(\frac{y_{(x,x)}}{1+{y_{(x)}}^2}-\frac{g\,{y_{(x)}}^2}{{v_0}^2-2\,g\,y}\right)^2\mathrm{d}x}
\end{aligned}&lt;&#x2F;script&gt;
&lt;p&gt;The author then proceeds to two approximations.  The first one is the
limit of &amp;quot;infinite velocity&amp;quot;, which seems more appropriate for some
drivers than some others, but is actually justified for everyone if we
compute the quantity &lt;script type=&quot;math&#x2F;tex&quot;&gt;\frac{{v_0}^2}{2\\,g\\,y} \approx 35
\gg 1&lt;&#x2F;script&gt;
.  This approximation allows to set &lt;script type=&quot;math&#x2F;tex&quot;&gt;g&lt;&#x2F;script&gt;

to &lt;script type=&quot;math&#x2F;tex&quot;&gt;0&lt;&#x2F;script&gt;
:&lt;&#x2F;p&gt;
&lt;script type=&quot;math&#x2F;tex;mode=display&quot;&gt;\begin{aligned}
\int_0^T{{a_y}^2\mathrm{d}t} &amp;\approx
\int_{-\ell}^{\ell}{\left(\frac{{v_0}^2}{1+{y_{(x)}}^2}\right)^{3&#x2F;2}
\left(\frac{y_{(x,x)}}{1+{y_{(x)}}^2}\right)^2\mathrm{d}x}\\
&amp;\approx
{v_0}^3\int_{-\ell}^{\ell}{\frac{{y_{(x,x)}}^2}{\left(1+{y_{(x)}}^2\right)^{7&#x2F;2}}\mathrm{d}x}
\end{aligned}&lt;&#x2F;script&gt;
&lt;p&gt;The second one is that of small angles, consisting of
neglecting &lt;script type=&quot;math&#x2F;tex&quot;&gt;y_{(x)}&lt;&#x2F;script&gt;
 in front of &lt;script type=&quot;math&#x2F;tex&quot;&gt;1&lt;&#x2F;script&gt;
.
This ones strips the criterion even further:&lt;&#x2F;p&gt;
&lt;script type=&quot;math&#x2F;tex;mode=display&quot;&gt;\begin{aligned}
\int_0^T{{a_y}^2\mathrm{d}t} &amp;\approx {v_0}^3\int_{-\ell}^{\ell}{{y_{(x,x)}}^2\mathrm{d}x}
\end{aligned}&lt;&#x2F;script&gt;
&lt;p&gt;such that the problem becomes tractable.  Indeed, applying the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Euler%E2%80%93Lagrange_equation&quot;&gt;Euler-Lagrange
equation&lt;&#x2F;a&gt; on the
Lagrangian &lt;script type=&quot;math&#x2F;tex&quot;&gt;\mathcal{L}(y,y_{(x)},y_{(x,x)}) =
{v_0}^3{y_{(x,x)}}^2-\lambda y&lt;&#x2F;script&gt;
 yields the optimal solution of this
twice simplified problem:&lt;&#x2F;p&gt;
&lt;script type=&quot;math&#x2F;tex;mode=display&quot;&gt;\frac{\ell}{A}y(x) = \frac{15}{16}\left(1-\left(\frac{x}{\ell}\right)^2\right)^2&lt;&#x2F;script&gt;
&lt;p&gt;He finally programs a stochastic optimizer with the exact equations, and
looks how this solution changes.  He didn&#x27;t observe many changes, and
concluded that the approximations were not too coarse.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;numerical-optimization-of-the-exact-problem&quot;&gt;Numerical optimization of the exact problem&lt;&#x2F;h1&gt;
&lt;p&gt;I decided to challenge this observation, and proceeded to submit
the problem to &lt;a href=&quot;https:&#x2F;&#x2F;www.bocop.org&#x2F;&quot;&gt;Bocop&lt;&#x2F;a&gt;, an optimal control
solver developed by the &lt;a href=&quot;https:&#x2F;&#x2F;www.inria.fr&#x2F;en&quot;&gt;Inria&lt;&#x2F;a&gt; team
&lt;a href=&quot;https:&#x2F;&#x2F;team.inria.fr&#x2F;commands&#x2F;&quot;&gt;COMMANDS&lt;&#x2F;a&gt;.  Bocop takes as input a
system of ordinary differential equations involving one or several
control variables, a criterion to optimize, and possibly constraints,
and determines the temporal profile of the control variables that
optimizes the criterion while satisfying the constraints.&lt;&#x2F;p&gt;
&lt;p&gt;For example, &lt;a href=&quot;https:&#x2F;&#x2F;www.bocop.org&#x2F;micro-swimmer-in-low-reynolds-number-fluid&#x2F;&quot;&gt;it is able to
determine&lt;&#x2F;a&gt;
how a model micro-swimmer should twist in order to move
fast.  Or how a rocket should &lt;a href=&quot;https:&#x2F;&#x2F;www.bocop.org&#x2F;goddard&#x2F;&quot;&gt;adapt its thrust during
flight&lt;&#x2F;a&gt; to go the highest.&lt;&#x2F;p&gt;
&lt;p&gt;Bocop is designed to models dynamical systems (with time derivatives),
and even though the equation of motion of the car involves time
derivatives, it seemed more natural here to remove the time and only
reason with the independent variable &lt;script type=&quot;math&#x2F;tex&quot;&gt;x&lt;&#x2F;script&gt;
.  Since we
go numeric, let&#x27;s make the problem dimensionless with the change of
variables &lt;script type=&quot;math&#x2F;tex&quot;&gt;\hat{x} = \frac{x}{\ell}&lt;&#x2F;script&gt;
, &lt;script type=&quot;math&#x2F;tex&quot;&gt;\hat{y} =
y\frac{\ell}{A}&lt;&#x2F;script&gt;
, &lt;script type=&quot;math&#x2F;tex&quot;&gt;\hat{A} =
\frac{A}{\ell^2}&lt;&#x2F;script&gt;
, &lt;script type=&quot;math&#x2F;tex&quot;&gt;\hat{g} = g\frac{\ell}{{v_0}^2}&lt;&#x2F;script&gt;
.
The criterion becomes&lt;&#x2F;p&gt;
&lt;script type=&quot;math&#x2F;tex;mode=display&quot;&gt;\frac{\ell}{\hat{A}^2\,{v_0}^3}\int_0^T{{a_y}^2\mathrm{d}t} =
\int_{-1}^1{\left(\frac{1-2\,\hat{A}\,\hat{g}\,\hat{y}}{1+\hat{A}^2{\hat{y}_{(\hat{x})}}^2}\right)^{3&#x2F;2}
\left(\frac{\hat{y}_{(\hat{x},\hat{x})}}{1+\hat{A}^2{\hat{y}_{(\hat{x})}}^2}-
\frac{\hat{A}\,\hat{g}\,{\hat{y}_{(\hat{x})}}^2}{1-2\,\hat{A}\,\hat{g}\,\hat{y}}\right)^2\mathrm{d}\hat{x}}&lt;&#x2F;script&gt;
&lt;p&gt;and from now on, I will drop all the hats and work only in reduced variables.&lt;&#x2F;p&gt;
&lt;p&gt;The system can be modelled with a system of four first-order
ordinary differential equations: &lt;script type=&quot;math&#x2F;tex&quot;&gt;y(x)&lt;&#x2F;script&gt;
 the profile
of the road, &lt;script type=&quot;math&#x2F;tex&quot;&gt;y_{(x)}(x)&lt;&#x2F;script&gt;
 its first
derivative, &lt;script type=&quot;math&#x2F;tex&quot;&gt;c(x)&lt;&#x2F;script&gt;
 the criterion and &lt;script type=&quot;math&#x2F;tex&quot;&gt;Y(x) =
\int_{-1}^x{y(u)\mathrm{d}u}&lt;&#x2F;script&gt;
 the quantity of concrete used.  The
control variable is &lt;script type=&quot;math&#x2F;tex&quot;&gt;y_{(x,x)}&lt;&#x2F;script&gt;
, the second derivative
of the profile.  The dynamics is straightforward:&lt;&#x2F;p&gt;
&lt;script type=&quot;math&#x2F;tex;mode=display&quot;&gt;\begin{aligned}
\frac{\mathrm{d}y}{\mathrm{d}x} &amp;= y_{(x)}\\
\frac{\mathrm{d}y_{(x)}}{\mathrm{d}x} &amp;= y_{(x,x)}\\
\frac{\mathrm{d}c}{\mathrm{d}x} &amp;=
\left(\frac{1-2\,A\,g\,y}{1+A^2{y_{(x)}}^2}\right)^{3&#x2F;2}
\left(\frac{y_{(x,x)}}{1+A^2{y_{(x)}}^2}-\frac{A\,g\,{y_{(x)}}^2}{1-2\,A\,g\,y}\right)^2\\
\frac{\mathrm{d}Y}{\mathrm{d}x} &amp;= y
\end{aligned}&lt;&#x2F;script&gt;
&lt;p&gt;and is implemented in the &lt;code&gt;dynamics.tpp&lt;&#x2F;code&gt; file&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;Tdouble yxx = control[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;Tdouble y = state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;Tdouble yx = state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;Tdouble c = state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;Tdouble Y = state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;double&lt;&#x2F;span&gt;&lt;span&gt; g = constants[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;double&lt;&#x2F;span&gt;&lt;span&gt; A = constants[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Tdouble vx = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;sqrt&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;*A*g*y)&#x2F;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;+A*A*yx*yx));
&lt;&#x2F;span&gt;&lt;span&gt;Tdouble ay = yxx&#x2F;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;+A*A*yx*yx) - A*g*yx*yx&#x2F;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;*A*g*y);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;state_dynamics[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;] = yx;             &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; dy&#x2F;dx
&lt;&#x2F;span&gt;&lt;span&gt;state_dynamics[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;] = yxx;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; dyx&#x2F;dx
&lt;&#x2F;span&gt;&lt;span&gt;state_dynamics[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;] = vx*vx*vx*ay*ay; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; dc&#x2F;dx
&lt;&#x2F;span&gt;&lt;span&gt;state_dynamics[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;] = y;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; dY&#x2F;dx
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The criterion is the final state of &lt;script type=&quot;math&#x2F;tex&quot;&gt;c&lt;&#x2F;script&gt;
:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span&gt;criterion = final_state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The boundary conditions take care of the continuity conditions,
initialize the criterion, and enforce the area constraint on the
profile:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c++&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-c++ &quot;&gt;&lt;code class=&quot;language-c++&quot; data-lang=&quot;c++&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;double&lt;&#x2F;span&gt;&lt;span&gt; A = constants[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;boundary_conditions[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;] = initial_state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; y(-1)  = 0
&lt;&#x2F;span&gt;&lt;span&gt;boundary_conditions[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;] =   final_state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;];      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; y(1)   = 0
&lt;&#x2F;span&gt;&lt;span&gt;boundary_conditions[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;] = initial_state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;];      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; yx(-1) = 0
&lt;&#x2F;span&gt;&lt;span&gt;boundary_conditions[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;] =   final_state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;];      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; yx(1)  = 0
&lt;&#x2F;span&gt;&lt;span&gt;boundary_conditions[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;] = initial_state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;];      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; c(-1)  = 0
&lt;&#x2F;span&gt;&lt;span&gt;boundary_conditions[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;] = initial_state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;];      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Y(-1)  = 0
&lt;&#x2F;span&gt;&lt;span&gt;boundary_conditions[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;] =   final_state[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;] - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Y(1)   = 1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally we can run the solver, let&#x27;s first check if we can reach the
analytical solution of the simple problem (&lt;code&gt;Tdouble vx = 1, ay = yxx;&lt;&#x2F;code&gt;):&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;f1.svg&quot; alt=&quot;Analytical and numerical solutions of the simplified problem&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Analytical and numerical solutions of the simplified problem.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;It seems to work, and the objective value &lt;script type=&quot;math&#x2F;tex&quot;&gt;22.5&lt;&#x2F;script&gt;
 is
consistent with what we could have expected from the analytical result.
Now let&#x27;s try the exact problem, with different values of &lt;script type=&quot;math&#x2F;tex&quot;&gt;A&lt;&#x2F;script&gt;

and &lt;script type=&quot;math&#x2F;tex&quot;&gt;g&lt;&#x2F;script&gt;
:&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;f1234.svg&quot; alt=&quot;Numerical solutions of the exact problem&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Numerical solutions of the exact problem.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;Note that &amp;quot;realistic&amp;quot; values of &lt;script type=&quot;math&#x2F;tex&quot;&gt;A&lt;&#x2F;script&gt;

and &lt;script type=&quot;math&#x2F;tex&quot;&gt;g&lt;&#x2F;script&gt;
 could be, respectively, &lt;script type=&quot;math&#x2F;tex&quot;&gt;0.1&lt;&#x2F;script&gt;

and &lt;script type=&quot;math&#x2F;tex&quot;&gt;0.15&lt;&#x2F;script&gt;
.  In this regime, the two approximations
(infinite speed, and small angles) are indeed very reasonable.  However,
for higher bumps, the exact solution starts to diverge noticeably from
the approximation.  Could we guess the analytical form of the solution?
That seems difficult in the general case, but we could try it in the
limit of infinite speed (&lt;script type=&quot;math&#x2F;tex&quot;&gt;g=0&lt;&#x2F;script&gt;
).  Actually, the
objective value for the solution calculated by Bocop for the
condition &lt;script type=&quot;math&#x2F;tex&quot;&gt;A=1&lt;&#x2F;script&gt;
, &lt;script type=&quot;math&#x2F;tex&quot;&gt;g=0&lt;&#x2F;script&gt;

is &lt;script type=&quot;math&#x2F;tex&quot;&gt;6.30&lt;&#x2F;script&gt;
, which is suspiciously close to an interesting
number, and the profile itself looks like four arcs of circles.
Coincidence?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;hypothesis-for-the-infinite-speed-limit&quot;&gt;Hypothesis for the infinite speed limit&lt;&#x2F;h1&gt;
&lt;p&gt;Let&#x27;s do the maths in the infinite speed limit, for the function&lt;&#x2F;p&gt;
&lt;script type=&quot;math&#x2F;tex;mode=display&quot;&gt;\mathrm{circles}(x) =
\begin{cases}
\frac{1}{2}-\sqrt{\frac{1}{4}-(x+1)^2}&amp;, -1 \leq x \leq -\frac{1}{2}\\
\frac{1}{2}+\sqrt{\frac{1}{4}-x^2}&amp;, -\frac{1}{2} \leq x \leq \frac{1}{2}\\
\frac{1}{2}-\sqrt{\frac{1}{4}-(x-1)^2}&amp;, \frac{1}{2} \leq x \leq 1
\end{cases}&lt;&#x2F;script&gt;
&lt;p&gt;The objective is reduced
to &lt;script type=&quot;math&#x2F;tex&quot;&gt;\mathrm{Obj}\_{g=0}[y] = \int_{-1}^{1}{\frac{{y_{(x,x)}}^2}{(1+A^2{y_{(x)}}^2)^{7&#x2F;2}}\mathrm{d}x}&lt;&#x2F;script&gt;
.&lt;&#x2F;p&gt;
&lt;p&gt;Mathematica helps and indeed &lt;script type=&quot;math&#x2F;tex&quot;&gt;\mathrm{Obj}_{A=1,g=0}[\mathrm{circles}] =
2\\,\pi&lt;&#x2F;script&gt;
.  To know if it is actually the optimal profile, we need to
plug it into the Euler-Lagrange equation.  Spoilers, it is not.&lt;&#x2F;p&gt;
&lt;p&gt;This profile is not optimal, but we can at least compare it
with &lt;script type=&quot;math&#x2F;tex&quot;&gt;\mathrm{approx}(x) = \frac{15}{16}(1-x^2)^2&lt;&#x2F;script&gt;
,
the analytical solution of the approximated problem.  Using the same
criterion, we get &lt;script type=&quot;math&#x2F;tex&quot;&gt;\mathrm{Opt}_{A=1,g=0}[\mathrm{approx}]
= 10.39&lt;&#x2F;script&gt;
, which proves that the four circles are a better profile for
the limit of infinite speed, at &lt;script type=&quot;math&#x2F;tex&quot;&gt;A=1&lt;&#x2F;script&gt;
.  We can also
compare these profiles as a function of &lt;script type=&quot;math&#x2F;tex&quot;&gt;A&lt;&#x2F;script&gt;
:&lt;&#x2F;p&gt;
&lt;figure&gt;
    &lt;img src=&quot;f_comparison.svg&quot; alt=&quot;Comparison of the fitnesses of the two profiles&quot; width=&quot;100%&quot;&gt;
        
        &lt;figcaption style=&quot;text-align: center&quot;&gt;&lt;em&gt;Comparison of the fitnesses of the two profiles.&lt;&#x2F;em&gt;&lt;&#x2F;figcaption&gt;
        
&lt;&#x2F;figure&gt;
&lt;p&gt;It was expected that the polynomial would win at low &lt;script type=&quot;math&#x2F;tex&quot;&gt;A&lt;&#x2F;script&gt;

since low &lt;script type=&quot;math&#x2F;tex&quot;&gt;A&lt;&#x2F;script&gt;
 means small angles.  As we have seen,
the solution in circles is better at high &lt;script type=&quot;math&#x2F;tex&quot;&gt;A&lt;&#x2F;script&gt;
.
What was more difficult to understand for me was that the
circles become infinitely bad at low &lt;script type=&quot;math&#x2F;tex&quot;&gt;A&lt;&#x2F;script&gt;

(like &lt;script type=&quot;math&#x2F;tex&quot;&gt;\frac{16}{15\\,a^4}&lt;&#x2F;script&gt;
).  I think that the reason for
this behaviour is the two points of infinite slope at &lt;script type=&quot;math&#x2F;tex&quot;&gt;x=
\pm\frac{1}{2}&lt;&#x2F;script&gt;
, which never disappear even when the profile
gets flattened by a decreasing &lt;script type=&quot;math&#x2F;tex&quot;&gt;A&lt;&#x2F;script&gt;
, and create two
discontinuity points where most of the acceleration is taken.&lt;&#x2F;p&gt;
&lt;p&gt;Can we find a better solution?  Is the optimal solution tractable in the
limit of infinite speed?  What if we wanted to minimize the maximum of
the acceleration over the period (the infinite norm) instead of the L2
norm?  Perhaps for another time!&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
