<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Algorithms on kenji.blog</title><link>http://kenji.blog/fr/categories/algorithms/</link><description>Recent content in Algorithms on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>fr</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 14:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/fr/categories/algorithms/index.xml" rel="self" type="application/rss+xml"/><item><title>Algorithmes de test de primalité rapides implémentés en C++ (méthode de Miller-Rabin, etc.)</title><link>http://kenji.blog/fr/p/cpp-fast-prime-testing-miller-rabin/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/cpp-fast-prime-testing-miller-rabin/</guid><description>&lt;img src="http://kenji.blog/p/cpp-fast-prime-testing-miller-rabin/img/eyecatch.jpg" alt="Featured image of post Algorithmes de test de primalité rapides implémentés en C++ (méthode de Miller-Rabin, etc.)" />&lt;h1 id="introduction--pourquoi-avons-nous-besoin-de-tests-de-primalité-rapides-">Introduction : Pourquoi avons-nous besoin de tests de primalité rapides ?
&lt;/h1>&lt;p>Dans le monde de l&amp;rsquo;informatique, de la théorie de la cryptographie ou de la programmation compétitive, déterminer rapidement et précisément &amp;ldquo;si un certain nombre est premier ou non&amp;rdquo; est un défi fondamental et extrêmement important. Par exemple, la cryptographie à clé publique telle que RSA, qui soutient la sécurité de la société Internet moderne, base sa sécurité sur la génération d&amp;rsquo;énormes nombres premiers et sur la difficulté de les multiplier (la difficulté de la factorisation en nombres premiers). Par conséquent, il n&amp;rsquo;est pas exagéré de dire que la technologie capable d&amp;rsquo;identifier instantanément si un nombre énorme est premier est la technologie qui soutient le fondement de la société numérique.&lt;/p>
&lt;p>De plus, en programmation compétitive (comme AtCoder et Codeforces), les tests de primalité sont un thème récurrent. Dans des situations où des dizaines de milliers de tests de primalité doivent être effectués en moins d&amp;rsquo;une seconde pour des entrées énormes avec des contraintes telles que $N \le 10^{18}$, les algorithmes traditionnels et naïfs ne peuvent absolument pas respecter la limite de temps de calcul (Time Limit Exceeded : TLE).&lt;/p>
&lt;p>Dans cet article, nous expliquerons en profondeur, de la base mathématique jusqu&amp;rsquo;aux implémentations hautement optimisées en C++, en commençant par des algorithmes naïfs de test de primalité, puis le &amp;ldquo;test de Fermat&amp;rdquo;, qui est un test de primalité probabiliste, et enfin l&amp;rsquo;algorithme rapide de niveau pratique le plus puissant qui surmonte ses faiblesses : le &amp;ldquo;test de primalité de Miller-Rabin&amp;rdquo;. En particulier, pour les entiers de 64 bits ($N &lt; 2^{64}$), nous expliquerons en détail la méthode qui ne se limite pas à un test probabiliste mais &amp;ldquo;peut tester la primalité avec 100% de certitude (test déterministe)&amp;rdquo;, et fournirons un code source en C++ prêt à être utilisé en pratique.&lt;/p>
&lt;hr>
&lt;h1 id="1-les-bases-du-test-de-primalité-et-la-méthode-des-divisions-successives-trial-division">1. Les bases du test de primalité et la méthode des divisions successives (Trial Division)
&lt;/h1>&lt;p>Un nombre premier (Prime number) est un entier naturel supérieur ou égal à 2 qui n&amp;rsquo;a de diviseurs positifs que 1 et lui-même. En suivant fidèlement la définition d&amp;rsquo;un nombre premier, pour déterminer si un certain entier $N$ est premier, nous pouvons essayer de diviser $N$ par tous les entiers de $2$ à $N-1$ ; s&amp;rsquo;il n&amp;rsquo;est jamais divisible, il est premier, et s&amp;rsquo;il est divisible au moins une fois, il est considéré comme un nombre composé (pas un nombre premier).&lt;/p>
&lt;p>Cependant, la complexité temporelle de cette méthode est de $O(N)$, et lorsque $N$ est un nombre énorme comme $10^{18}$, même les ordinateurs modernes prendront énormément de temps pour le calcul.&lt;/p>
&lt;h2 id="optimisation-de-la-méthode-des-divisions-successives--recherche-jusquà-sqrtn">Optimisation de la méthode des divisions successives : recherche jusqu&amp;rsquo;à $\sqrt{N}$
&lt;/h2>&lt;p>Lorsqu&amp;rsquo;un nombre composé $N$ est exprimé comme $a \times b = N$ ($a \le b$), il est certain que $a \le \sqrt{N}$. Par conséquent, il n&amp;rsquo;est pas nécessaire d&amp;rsquo;exécuter la boucle du test de primalité jusqu&amp;rsquo;à $N-1$ ; il suffit de vérifier jusqu&amp;rsquo;à $\sqrt{N}$.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Test de primalité par la méthode des divisions successives (O(sqrt(N)))
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">is_prime_trial_division&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Vérifier uniquement les nombres impairs supérieurs ou égaux à 3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>La complexité de cet algorithme est de $O(\sqrt{N})$. Si $N \le 10^{12}$, il peut être calculé en un instant, mais dans le cas de $N \approx 10^{18}$, le nombre de boucles devient d&amp;rsquo;environ $10^9$ fois, ce qui prend de quelques centaines de millisecondes à quelques secondes même avec un environnement C++, et n&amp;rsquo;est donc pas adapté pour de multiples évaluations.&lt;/p>
&lt;hr>
&lt;h1 id="2-le-test-de-fermat--laube-du-test-de-primalité-probabiliste">2. Le test de Fermat : L&amp;rsquo;aube du test de primalité probabiliste
&lt;/h1>&lt;p>Ce qui a été conçu pour surmonter les limites de la méthode des divisions successives est l&amp;rsquo;&amp;ldquo;algorithme probabiliste (Probabilistic Algorithm)&amp;rdquo; qui utilise des théorèmes de la théorie des nombres. L&amp;rsquo;exemple typique en est le &amp;ldquo;test de primalité de Fermat&amp;rdquo; utilisant le petit théorème de Fermat.&lt;/p>
&lt;h2 id="le-petit-théorème-de-fermat-fermats-little-theorem">Le petit théorème de Fermat (Fermat&amp;rsquo;s Little Theorem)
&lt;/h2>&lt;p>Ce théorème, découvert par Pierre de Fermat, affirme ce qui suit :&lt;/p>
&lt;blockquote>
&lt;p>Pour tout nombre premier $p$ et tout entier $a$ qui lui est premier (non multiple de $p$), la congruence suivante est vraie :
&lt;/p>
$$ a^{p-1} \equiv 1 \pmod p $$
&lt;/blockquote>
&lt;p>En prenant la contraposée de ce théorème, on peut dire que &amp;ldquo;pour un certain entier $N$ et un entier $a$ premier avec $N$, si $a^{N-1} \not\equiv 1 \pmod N$, alors $N$ est définitivement un nombre composé&amp;rdquo;. En utilisant cette propriété, le test de Fermat consiste à choisir une base (base) aléatoire $a$ pour le nombre cible $N$, à calculer $a^{N-1} \pmod N$ et à vérifier s&amp;rsquo;il devient $1$.&lt;/p>
&lt;h2 id="exponentiation-modulaire-rapide-méthode-dexponentiation-binaire">Exponentiation modulaire rapide (Méthode d&amp;rsquo;exponentiation binaire)
&lt;/h2>&lt;p>Pour effectuer le test de Fermat, il est nécessaire de calculer rapidement d&amp;rsquo;énormes puissances telles que $a^{N-1} \pmod N$. Pour cela, nous utilisons la &amp;ldquo;méthode d&amp;rsquo;exponentiation binaire (Modular Exponentiation / Binary Exponentiation)&amp;rdquo;. La complexité est de $O(\log N)$, ce qui est extrêmement rapide.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Calcul de a^b mod m en utilisant la méthode d&amp;#39;exponentiation binaire
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="nf">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">long&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">res&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">__int128_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">b&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>Ici, pour éviter les dépassements de capacité, l&amp;rsquo;extension GCC/Clang &lt;code>__int128_t&lt;/code> (entier de 128 bits) est utilisée pour conserver les produits intermédiaires.&lt;/li>
&lt;/ul>
&lt;h2 id="nombres-pseudo-premiers-et-nombres-de-carmichael-carmichael-numbers">Nombres pseudo-premiers et nombres de Carmichael (Carmichael Numbers)
&lt;/h2>&lt;p>Le test de Fermat est très puissant, mais il a une faiblesse fatale. Il existe des nombres tels que bien que $N$ soit un nombre composé, $a^{N-1} \equiv 1 \pmod N$ reste vrai pour tous les $a$ ($a$ premiers avec $N$).&lt;/p>
&lt;p>De tels nombres sont appelés &amp;ldquo;nombres pseudo-premiers absolus&amp;rdquo; ou &amp;ldquo;nombres de Carmichael&amp;rdquo;. Le plus petit nombre de Carmichael est $561 = 3 \times 11 \times 17$.
En raison de la présence de nombres de Carmichael, il n&amp;rsquo;est pas possible d&amp;rsquo;effectuer un test déterministe avec une &amp;ldquo;probabilité de 100%&amp;rdquo; en utilisant uniquement le test de Fermat. Peu importe le nombre de bases différentes $a$ que vous essayez, un nombre comme $561$ fera toujours semblant d&amp;rsquo;être premier (une tromperie).&lt;/p>
&lt;hr>
&lt;h1 id="3-test-de-primalité-de-miller-rabin-miller-rabin-primality-test">3. Test de primalité de Miller-Rabin (Miller-Rabin Primality Test)
&lt;/h1>&lt;p>Le &amp;ldquo;test de primalité de Miller-Rabin&amp;rdquo;, conçu par Miller (Gary L. Miller) et Rabin (Michael O. Rabin), a brillamment surmonté la faiblesse du test de Fermat (l&amp;rsquo;existence des nombres de Carmichael).
Aujourd&amp;rsquo;hui, il est le plus largement utilisé dans la génération de clés des systèmes cryptographiques et des bibliothèques internes de divers langages de programmation en tant algorithme de test de primalité rapide et pratique.&lt;/p>
&lt;h2 id="principes-mathématiques">Principes mathématiques
&lt;/h2>&lt;p>En plus du petit théorème de Fermat, l&amp;rsquo;algorithme de Miller-Rabin utilise la propriété selon laquelle &amp;ldquo;dans un corps fini modulo un nombre premier ($\mathbb{Z}/p\mathbb{Z}$), les seules solutions à $x^2 \equiv 1 \pmod p$ sont $x \equiv 1$ ou $x \equiv -1$&amp;rdquo; (dans le cas modulo un nombre composé, d&amp;rsquo;autres racines carrées non triviales peuvent exister).&lt;/p>
&lt;p>Si l&amp;rsquo;on soustrait $1$ au nombre impair $N$ que nous voulons tester, $N-1$ sera toujours un nombre pair. Par conséquent, nous divisons $N-1$ par $2$ autant de fois que possible, et l&amp;rsquo;exprimons sous la forme suivante :
&lt;/p>
$$ N-1 = d \cdot 2^s $$
&lt;p>
(Ici, $d$ est un nombre impair, $s \ge 1$)&lt;/p>
&lt;p>Pour toute base arbitraire $a$ ($1 &lt; a &lt; N-1$), nous vérifions si $a^{N-1} \equiv 1 \pmod N$ selon le petit théorème de Fermat, mais nous effectuons ce calcul par étapes.
Plus précisément, nous répétons la mise au carré dans l&amp;rsquo;ordre : $a^d, a^{d \cdot 2}, a^{d \cdot 4}, \ldots, a^{d \cdot 2^s}$.&lt;/p>
&lt;p>La condition pour que le test de Miller-Rabin détermine que $N$ est &amp;ldquo;premier (ou probablement premier avec une forte probabilité)&amp;rdquo; est que l&amp;rsquo;une des conditions &lt;strong>suivantes&lt;/strong> soit remplie.&lt;/p>
&lt;ol>
&lt;li>$a^d \equiv 1 \pmod N$&lt;/li>
&lt;li>Il existe un certain $r$ ($0 \le r &lt; s$) tel que $a^{d \cdot 2^r} \equiv -1 \pmod N$ soit vrai.
&lt;ul>
&lt;li>En arithmétique modulaire en C++, $-1 \pmod N$ devient $N-1$.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Si $N$ est premier, cette condition sera toujours remplie pour n&amp;rsquo;importe quel $a$. Inversement, si $N$ est un nombre composé, lorsqu&amp;rsquo;un $a$ aléatoire est choisi, il est prouvé mathématiquement que la probabilité de remplir cette condition (la probabilité d&amp;rsquo;être trompé) est inférieure ou égale à $\frac{1}{4}$.
En effectuant $k$ tests indépendants, la probabilité d&amp;rsquo;une fausse identification devient inférieure ou égale à $\left(\frac{1}{4}\right)^k$, ce qui peut être considéré comme pratiquement nul. Il n&amp;rsquo;existe pas de nombres qui puissent &amp;ldquo;tromper de manière absolue&amp;rdquo; comme les nombres de Carmichael.&lt;/p>
&lt;h2 id="flux-de-lalgorithme-de-la-méthode-de-miller-rabin-organigramme-mermaid">Flux de l&amp;rsquo;algorithme de la méthode de Miller-Rabin (Organigramme Mermaid)
&lt;/h2>&lt;p>Le diagramme ci-dessous illustre le flux logique d&amp;rsquo;un seul test (un test pour une base $a$) de la méthode de test de primalité de Miller-Rabin.&lt;/p>
&lt;div class="mermaid">graph TD
Start["Début du test (Entrée : N, a)"] --> CalcDS["Calculer d (impair) et s tels que N-1 = d * 2^s"]
CalcDS --> CalcX["Calculer x = a^d mod N"]
CalcX --> CheckX1{"x == 1 ou x == N-1 ?"}
CheckX1 -- "Oui" --> ReturnTrue["Peut-être premier (Probably Prime)"]
CheckX1 -- "Non" --> LoopStart["Début de la boucle de r = 1 à s-1"]
LoopStart --> LoopCondition{"r &lt; s ?"}
LoopCondition -- "Non" --> ReturnFalse["Certainement composé (Composite)"]
LoopCondition -- "Oui" --> SquareX["Calculer x = (x * x) mod N"]
SquareX --> CheckXMinus1{"x == N - 1 ?"}
CheckXMinus1 -- "Oui" --> ReturnTrue
CheckXMinus1 -- "Non" --> CheckXOne{"x == 1 ?"}
CheckXOne -- "Oui" --> ReturnFalse
CheckXOne -- "Non" --> LoopNext["Incrémenter r de 1 et continuer"]
LoopNext --> LoopCondition&lt;/div>
&lt;hr>
&lt;h1 id="4-test-déterministe-pour-les-entiers-de-64-bits">4. Test déterministe pour les entiers de 64 bits
&lt;/h1>&lt;p>Bien que le test de primalité de Miller-Rabin soit fondamentalement un algorithme &amp;ldquo;probabiliste&amp;rdquo;, lorsque la limite supérieure de $N$ est fixe, on peut tester la primalité avec &amp;ldquo;100% de certitude&amp;rdquo; en essayant toutes les bases spécifiques $a$ (bases).
Ceci est appelé le &lt;strong>test de Miller-Rabin déterministe (Deterministic Miller-Rabin Test)&lt;/strong>.&lt;/p>
&lt;p>Grâce aux recherches de Jim Sinclair et d&amp;rsquo;autres, il a été révélé que pour tous les entiers $N &lt; 2^{64}$ (environ $1.8 \times 10^{19}$), un test complètement déterministe et suffisant est possible en choisissant les $7$ nombres premiers suivants comme base $a$.&lt;/p>
&lt;p>&lt;strong>Liste des bases $a$ à tester :&lt;/strong>
&lt;code>{2, 325, 9375, 28178, 450775, 9780504, 1795265022}&lt;/code>&lt;/p>
&lt;p>Alternativement, en utilisant un autre ensemble bien connu des $12$ nombres premiers suivants, une détermination parfaite peut également être effectuée en dessous de $N &lt; 2^{64}$.
&lt;code>{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}&lt;/code>&lt;/p>
&lt;p>Cette fois, pour améliorer la simplicité et la fiabilité de l&amp;rsquo;algorithme, nous adopterons la méthode utilisant ces derniers $12$ nombres premiers comme bases (ou les $7$ bases plus optimisées). Dans l&amp;rsquo;implémentation C++, en divisant la plage avec un branchement conditionnel, nous optimiserons pour minimiser le nombre de tests.&lt;/p>
&lt;hr>
&lt;h1 id="5-implémentation-avancée-en-c-highly-optimized-c-implementation">5. Implémentation avancée en C++ (Highly Optimized C++ Implementation)
&lt;/h1>&lt;p>Maintenant, en résumant les théories mathématiques et la conception des algorithmes discutées jusqu&amp;rsquo;à présent, nous présenterons le code d&amp;rsquo;implémentation de la fonction de test de primalité de Miller-Rabin au niveau le plus fort dans le C++ moderne.&lt;/p>
&lt;h2 id="points-clés-de-limplémentation">Points clés de l&amp;rsquo;implémentation
&lt;/h2>&lt;ol>
&lt;li>
&lt;p>&lt;strong>Éviter le dépassement de capacité de la multiplication des entiers de 64 bits :&lt;/strong>
Dans le cas de $N \approx 10^{18}$, $x \times x$ dans la multiplication modulaire sera au maximum de $10^{36}$, ce qui dépassera facilement la valeur maximale de $1.8 \times 10^{19}$ d&amp;rsquo;un entier normal de 64 bits (&lt;code>uint64_t&lt;/code> ou &lt;code>long long&lt;/code>).
Pour résoudre ce problème, le type d&amp;rsquo;extension GCC ou Clang &lt;code>__int128_t&lt;/code> (ou &lt;code>unsigned __int128&lt;/code>) est utilisé, et le calcul est effectué avec une précision de 128 bits avant de prendre le modulo. Cela permet une multiplication modulaire à grande vitesse sans l&amp;rsquo;utilisation d&amp;rsquo;algorithmes complexes.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Choix de la base déterministe :&lt;/strong>
Si la valeur de $N$ est petite, elle est optimisée de manière à ce qu&amp;rsquo;un plus petit nombre de bases n&amp;rsquo;aient besoin d&amp;rsquo;être testées.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="code-source-c-complet">Code source C++ complet
&lt;/h2>&lt;p>Voici le code source complet prêt à être utilisé en pratique. Ce code peut être copié et utilisé tel quel dans des environnements tels que la programmation compétitive.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;span class="lnt">94
&lt;/span>&lt;span class="lnt">95
&lt;/span>&lt;span class="lnt">96
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstdint&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;initializer_list&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// (a * b) mod m rapide avec des entiers de 128 bits
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">inline&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="nf">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="p">)((&lt;/span>&lt;span class="kt">unsigned&lt;/span> &lt;span class="n">__int128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Calcul de (base^exp) mod m par la méthode d&amp;#39;exponentiation binaire
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nf">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">exp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">base&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exp&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Test déterministe pour les entiers 64 bits par la méthode de Miller-Rabin
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="nf">is_prime_miller_rabin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Valeurs limites et évaluation préalable des petits nombres premiers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">5&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">5&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">7&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Décomposer sous la forme n-1 = d * 2^s
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">d&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">s&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">d&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Liste des bases utilisées pour le test
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Optimisation pour minimiser le nombre de bases testées selon la taille de N
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">bases&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">4759123141ULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">61&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">1122004669633ULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">23&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1662803&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 7 bases qui sont déterministes pour tous les nombres N &amp;lt; 2^64
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">bases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">325&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">9375&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">28178&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">450775&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">9780504&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1795265022&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Exécuter le test pour chaque base
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nl">a&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">bases&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">%=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Si a est un multiple de n, cela ne peut pas être testé, mais ce n&amp;#39;est pas premier
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint64_t&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_pow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Première condition remplie, passer à la base suivante
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="n">composite&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Boucle de s-1 itérations (x = x^2 mod n)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">r&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">mod_mul&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">composite&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Seconde condition remplie, peut-être premier
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Si aucune condition n&amp;#39;est remplie, il est certainement composé
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">composite&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Si la condition est remplie pour toutes les bases, il est certainement premier
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Échantillons pour les tests
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">test_cases&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="mi">1000000007&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Nombre premier célèbre
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">998244353&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Nombre premier célèbre
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">1000000000000000003&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Nombre premier autour de 10^18
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">1000000000000000007&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Nombre composé (10^18 + 7)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">561&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Nombre de Carmichael (Composé)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="mi">18446744073709551557ULL&lt;/span> &lt;span class="c1">// Un des plus grands nombres premiers près de 2^64
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span> &lt;span class="nl">n&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">test_cases&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; is &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">is_prime_miller_rabin&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s">&amp;#34;Prime&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s">&amp;#34;Composite&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h1 id="6-évaluation-de-la-complexité-et-des-performances-de-lalgorithme">6. Évaluation de la complexité et des performances de l&amp;rsquo;algorithme
&lt;/h1>&lt;p>Examinons les performances de l&amp;rsquo;algorithme implémenté.&lt;/p>
&lt;h2 id="complexité-temporelle-time-complexity">Complexité temporelle (Time Complexity)
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>Méthode des divisions successives :&lt;/strong> $O(\sqrt{N})$&lt;/li>
&lt;li>&lt;strong>Test de Fermat :&lt;/strong> Calcul d&amp;rsquo;exponentiation $O(\log N) \times k$ ($k$ étant le nombre d&amp;rsquo;essais)&lt;/li>
&lt;li>&lt;strong>Méthode de Miller-Rabin :&lt;/strong> Calcul d&amp;rsquo;exponentiation et boucle $O(\log N) \times k$&lt;/li>
&lt;/ul>
&lt;p>Dans un environnement 64 bits ($N \le 2^{64}$), la méthode déterministe de Miller-Rabin ci-dessus vérifie au maximum $7$ bases. Par conséquent, $k$ peut être considéré comme une constante $k \le 7$, et la complexité temporelle globale devient strictement $O(\log N)$.
Même dans le pire des cas ($N \approx 10^{19}$), le nombre d&amp;rsquo;étapes d&amp;rsquo;exécution tient dans un maximum de $7 \times 64 = 448$ opérations de base, et le temps d&amp;rsquo;exécution est inférieur à quelques microsecondes ($10^{-6}$ secondes). Par rapport au $O(\sqrt{N})$ de la méthode des divisions successives (environ $4 \times 10^9$ boucles), une &lt;strong>accélération de plusieurs millions de fois&lt;/strong> est atteinte.&lt;/p>
&lt;h2 id="optimisation-supplémentaire--multiplication-de-montgomery-montgomery-multiplication">Optimisation supplémentaire : Multiplication de Montgomery (Montgomery Multiplication)
&lt;/h2>&lt;p>Dans l&amp;rsquo;implémentation de cet article, le type étendu entier de 128 bits &lt;code>__int128_t&lt;/code> est utilisé pour la division (opération modulo &lt;code>%&lt;/code>). Même sur les processeurs modernes, la division entière (instruction DIV) est une instruction coûteuse qui nécessite des dizaines de cycles par rapport à l&amp;rsquo;addition ou la multiplication.&lt;/p>
&lt;p>Les créateurs de bibliothèques et les programmeurs compétitifs cherchant à atteindre les limites de l&amp;rsquo;optimisation adoptent parfois une méthode appelée &lt;strong>multiplication de Montgomery (Montgomery Multiplication)&lt;/strong>. La multiplication de Montgomery est un algorithme remarquable qui remplace les opérations modulo coûteuses (division) par &amp;ldquo;seulement des décalages de bits et des multiplications&amp;rdquo; en mappant les nombres dans un &amp;ldquo;espace de Montgomery&amp;rdquo; spécial.
En intégrant cela dans la multiplication modulaire du test de Miller-Rabin, la vitesse d&amp;rsquo;exécution peut encore être augmentée d&amp;rsquo;environ 2 à 3 fois. Comme c&amp;rsquo;est un sujet très profond, nous l&amp;rsquo;expliquerons en détail dans un autre article.&lt;/p>
&lt;hr>
&lt;h1 id="7-conclusion">7. Conclusion
&lt;/h1>&lt;p>Dans cet article, nous avons expliqué d&amp;rsquo;un coup de nombreux concepts, des bases aux sujets avancés du test de primalité.
Récapitulons les points clés.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>La méthode des divisions successives&lt;/strong> est certaine, mais manque d&amp;rsquo;utilité pratique lorsque $N$ dépasse $10^{12}$ en raison de sa complexité de $O(\sqrt{N})$.&lt;/li>
&lt;li>&lt;strong>Le test de Fermat&lt;/strong> est extrêmement rapide avec $O(\log N)$, mais a une faiblesse fatale en étant trompé par des nombres pseudo-premiers absolus tels que les nombres de Carmichael.&lt;/li>
&lt;li>&lt;strong>Le test de primalité de Miller-Rabin&lt;/strong> est l&amp;rsquo;algorithme le plus puissant et pratique qui résout les faiblesses du test de Fermat.&lt;/li>
&lt;li>Dans l&amp;rsquo;implémentation C++, le dépassement de capacité de multiplication d&amp;rsquo;entiers 64 bits peut être géré en toute sécurité en utilisant &lt;code>__int128_t&lt;/code>.&lt;/li>
&lt;li>Dans la plage des entiers de 64 bits ($N &lt; 2^{64}$), en choisissant $7$ ou $12$ nombres premiers spécifiques comme base, un &lt;strong>test de primalité déterministe (100% précis)&lt;/strong> plutôt que probabiliste est possible.&lt;/li>
&lt;/ol>
&lt;p>Le test de primalité rapide est une technologie incontournable dans les calculs manipulant de grands nombres. Le code source en C++ de Miller-Rabin fourni dans cet article est robuste et peut être utilisé en pratique tel quel. N&amp;rsquo;hésitez pas à l&amp;rsquo;utiliser dans vos propres projets ou compétitions d&amp;rsquo;algorithmes.&lt;/p>
&lt;div class="mermaid">graph LR
TrialDivision["Méthode des divisions successives (O(√N))"] --> Fermat["Test de Fermat (O(log N), avec faiblesses)"]
Fermat --> MillerRabin["Méthode de Miller-Rabin (O(log N), rendable déterministe)"]
MillerRabin --> Montgomery["+ Multiplication de Montgomery (Accélération constante)"]
style MillerRabin fill:#f9f,stroke:#333,stroke-width:2px&lt;/div>
&lt;p>Le monde des algorithmes, où se croisent la programmation et les mathématiques, est extrêmement beau et profond. J&amp;rsquo;espère que cela vous sera utile dans votre apprentissage futur.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Reference:&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;em>Pomerance, C., Selfridge, J. L., &amp;amp; Wagstaff, S. S. (1980). The pseudoprimes to 25.10^9. Mathematics of Computation.&lt;/em>&lt;/li>
&lt;li>&lt;em>Sinclair, J. (2011). Deterministic Miller-Rabin primality testing.&lt;/em>&lt;/li>
&lt;/ul></description></item></channel></rss>