{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Source sur la loi de réciprocité quadratique et le symbole de Legendre :\n", " \n", "" ] }, { "cell_type": "code", "execution_count": 277, "metadata": {}, "outputs": [], "source": [ "def decomposition_premiers(n : int) -> list:\n", " \"\"\"Décompose l'entier n en facteurs premiers\n", " \"\"\"\n", " d = 2\n", " decomposition = []\n", " if n < 0:\n", " decomposition.append((-1,1))\n", " n = -n\n", " while n > 1:\n", " while n % d != 0:\n", " d += 1\n", " multiplicite = 0\n", " while n % d == 0:\n", " multiplicite += 1\n", " n = n // d\n", " decomposition.append((d, multiplicite))\n", " return decomposition\n", "\n", "def signe_reciprocite_quadratique(p, q):\n", " \"\"\"Renvoie le signe du coefficient de changement de signe\n", " (-1) ** ((p-1) // 2 * (q - 1)//2) dans la loi de réciprocité quadratique\"\"\"\n", " if (p - 1) * (q - 1) % 8 == 0:\n", " return 1\n", " else:\n", " return -1\n", " \n", "def symbole_legendre(p, q, debug = False):\n", " \"\"\"Renvoie le symbole de Legendre de p par rapport à q\n", " 0 (cas trivial) si q divise p\n", " 1 si p possède un résidu quadratique modulo q\n", " -1 si p ne possède pas de résidu quadratique modulo q\n", " \"\"\"\n", " \n", " \n", " memo = dict() #dictionnaire de memoization des symboles de Legendre déjà calculés\n", " compteur_appels = 0\n", " \n", " def auxiliaire(p, q):\n", " nonlocal compteur_appels\n", " if debug:\n", " print(f\"Symbole de Legendre (p|q) avec p={p}, q={q}, décomposition en facteurs premiers de {p} : {decomposition_premiers(p)}\")\n", " compteur_appels += 1\n", " if p % q == 0:\n", " return 0\n", " if (p, q) in memo:\n", " return memo[(p,q)]\n", " if p == 1:\n", " memo[(p,q)] = 1\n", " elif p == -1:\n", " if q % 4 == 1:\n", " memo[(p,q)] = 1\n", " else:\n", " memo[(p,q)] = -1\n", " elif p == 2:\n", " if q % 8 in [1, 7]:\n", " memo[(p,q)] = 1\n", " else:\n", " memo[(p,q)] = -1 \n", " else:\n", " symbole = 1\n", " for (facteur_premier, multiplicite) in decomposition_premiers(p):\n", " if multiplicite % 2 == 1: \n", " if facteur_premier == 2:\n", " symbole = symbole * auxiliaire(2, q)\n", " else: #la loi de réciprocité quadratique ne s'applique qu'avec p et q pairs\n", " signe = signe_reciprocite_quadratique(facteur_premier, q) \n", " symbole = symbole * auxiliaire(q % facteur_premier, facteur_premier) * signe \n", " \n", " memo[(p, q)] = symbole\n", " return memo[(p, q)] \n", "\n", " calcul = auxiliaire(p, q) \n", " if debug:\n", " print(f\"Calcul en {compteur_appels} appels\")\n", " return calcul" ] }, { "cell_type": "code", "execution_count": 278, "metadata": {}, "outputs": [], "source": [ "def resolution_residu_quadratique(p, q, debug = False):\n", " \"\"\"Renvoie une liste (x, y) de solutions de l'équation q * x + p = y ** 2\n", " liste vide si p n'est pas un résidu quadratique modulo q\"\"\"\n", " #premier cas : pas de solution\n", " if symbole_legendre(p,q) == -1:\n", " return []\n", " #second cas \n", " else:\n", " #recherche des deux solutions avec 0 <= y < q par balayage \n", " p = p % q\n", " y = 0\n", " carre_y = y ** 2\n", " while carre_y % q != p:\n", " y += 1\n", " carre_y = (y ** 2) % q\n", " solution = [((carre_y - p) // q, y), (((q - y) ** 2 - p) // q, q - y)]\n", " if debug:\n", " for (x, y) in solution:\n", " print(f\"{q} * {x} + {p} = {y} ** 2 = > {q * x + p == y ** 2}\")\n", " return solution" ] }, { "cell_type": "code", "execution_count": 279, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=78, q=2, décomposition en facteurs premiers de 78 : [(2, 1), (3, 1), (13, 1)]\n", "Calcul en 1 appels\n" ] }, { "data": { "text/plain": [ "0" ] }, "execution_count": 279, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(78,2, debug = True) #cas trivial" ] }, { "cell_type": "code", "execution_count": 280, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=2, q=103, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Calcul en 1 appels\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 280, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(2,103, debug = True)" ] }, { "cell_type": "code", "execution_count": 281, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=2, q=103, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Calcul en 1 appels\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 281, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(2,103, debug = True)" ] }, { "cell_type": "code", "execution_count": 282, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=3, q=103, décomposition en facteurs premiers de 3 : [(3, 1)]\n", "Symbole de Legendre (p|q) avec p=1, q=3, décomposition en facteurs premiers de 1 : []\n", "Calcul en 2 appels\n" ] }, { "data": { "text/plain": [ "-1" ] }, "execution_count": 282, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(3, 103 , debug = True)" ] }, { "cell_type": "code", "execution_count": 283, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=12, q=103, décomposition en facteurs premiers de 12 : [(2, 2), (3, 1)]\n", "Symbole de Legendre (p|q) avec p=1, q=3, décomposition en facteurs premiers de 1 : []\n", "Calcul en 2 appels\n" ] }, { "data": { "text/plain": [ "-1" ] }, "execution_count": 283, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(12, 103 , debug = True)" ] }, { "cell_type": "code", "execution_count": 284, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=3, q=13, décomposition en facteurs premiers de 3 : [(3, 1)]\n", "Symbole de Legendre (p|q) avec p=1, q=3, décomposition en facteurs premiers de 1 : []\n", "Calcul en 2 appels\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 284, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(3, 13 , debug = True)" ] }, { "cell_type": "code", "execution_count": 285, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=78, q=103, décomposition en facteurs premiers de 78 : [(2, 1), (3, 1), (13, 1)]\n", "Symbole de Legendre (p|q) avec p=2, q=103, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Symbole de Legendre (p|q) avec p=1, q=3, décomposition en facteurs premiers de 1 : []\n", "Symbole de Legendre (p|q) avec p=12, q=13, décomposition en facteurs premiers de 12 : [(2, 2), (3, 1)]\n", "Symbole de Legendre (p|q) avec p=1, q=3, décomposition en facteurs premiers de 1 : []\n", "Calcul en 5 appels\n" ] }, { "data": { "text/plain": [ "-1" ] }, "execution_count": 285, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(78,103, debug = True)" ] }, { "cell_type": "code", "execution_count": 286, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 286, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resolution_residu_quadratique(78, 103, debug = True)" ] }, { "cell_type": "code", "execution_count": 287, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=11, q=89, décomposition en facteurs premiers de 11 : [(11, 1)]\n", "Symbole de Legendre (p|q) avec p=1, q=11, décomposition en facteurs premiers de 1 : []\n", "Calcul en 2 appels\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 287, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(11,89, debug = True)" ] }, { "cell_type": "code", "execution_count": 288, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "89 * 0 + 11 = 10 ** 2 = > False\n", "89 * 70 + 11 = 79 ** 2 = > True\n" ] }, { "data": { "text/plain": [ "[(0, 10), (70, 79)]" ] }, "execution_count": 288, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resolution_residu_quadratique(11, 89, debug = True)" ] }, { "cell_type": "code", "execution_count": 289, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=713, q=1009, décomposition en facteurs premiers de 713 : [(23, 1), (31, 1)]\n", "Symbole de Legendre (p|q) avec p=20, q=23, décomposition en facteurs premiers de 20 : [(2, 2), (5, 1)]\n", "Symbole de Legendre (p|q) avec p=3, q=5, décomposition en facteurs premiers de 3 : [(3, 1)]\n", "Symbole de Legendre (p|q) avec p=2, q=3, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Symbole de Legendre (p|q) avec p=17, q=31, décomposition en facteurs premiers de 17 : [(17, 1)]\n", "Symbole de Legendre (p|q) avec p=14, q=17, décomposition en facteurs premiers de 14 : [(2, 1), (7, 1)]\n", "Symbole de Legendre (p|q) avec p=2, q=17, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Symbole de Legendre (p|q) avec p=3, q=7, décomposition en facteurs premiers de 3 : [(3, 1)]\n", "Symbole de Legendre (p|q) avec p=1, q=3, décomposition en facteurs premiers de 1 : []\n", "Calcul en 9 appels\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 289, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(713,1009, True)" ] }, { "cell_type": "code", "execution_count": 290, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1009 * 0 + 713 = 210 ** 2 = > False\n", "1009 * 632 + 713 = 799 ** 2 = > True\n" ] }, { "data": { "text/plain": [ "[(0, 210), (632, 799)]" ] }, "execution_count": 290, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resolution_residu_quadratique(713, 1009, debug = True)" ] }, { "cell_type": "code", "execution_count": 291, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=3, q=5, décomposition en facteurs premiers de 3 : [(3, 1)]\n", "Symbole de Legendre (p|q) avec p=2, q=3, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Calcul en 2 appels\n" ] }, { "data": { "text/plain": [ "-1" ] }, "execution_count": 291, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(3,5, True)" ] }, { "cell_type": "code", "execution_count": 292, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 292, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resolution_residu_quadratique(3, 5, debug = True)" ] }, { "cell_type": "code", "execution_count": 293, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=17, q=31, décomposition en facteurs premiers de 17 : [(17, 1)]\n", "Symbole de Legendre (p|q) avec p=14, q=17, décomposition en facteurs premiers de 14 : [(2, 1), (7, 1)]\n", "Symbole de Legendre (p|q) avec p=2, q=17, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Symbole de Legendre (p|q) avec p=3, q=7, décomposition en facteurs premiers de 3 : [(3, 1)]\n", "Symbole de Legendre (p|q) avec p=1, q=3, décomposition en facteurs premiers de 1 : []\n", "Calcul en 5 appels\n" ] }, { "data": { "text/plain": [ "-1" ] }, "execution_count": 293, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(17,31, True)" ] }, { "cell_type": "code", "execution_count": 294, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 294, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resolution_residu_quadratique(17, 31, debug = True)" ] }, { "cell_type": "code", "execution_count": 295, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=5984, q=7907, décomposition en facteurs premiers de 5984 : [(2, 5), (11, 1), (17, 1)]\n", "Symbole de Legendre (p|q) avec p=2, q=7907, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Symbole de Legendre (p|q) avec p=9, q=11, décomposition en facteurs premiers de 9 : [(3, 2)]\n", "Symbole de Legendre (p|q) avec p=2, q=17, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Calcul en 4 appels\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 295, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(5984,7907, True)" ] }, { "cell_type": "code", "execution_count": 296, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7907 * 0 + 5984 = 364 ** 2 = > False\n", "7907 * 7195 + 5984 = 7543 ** 2 = > True\n" ] }, { "data": { "text/plain": [ "[(0, 364), (7195, 7543)]" ] }, "execution_count": 296, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resolution_residu_quadratique(5984,7907, debug = True)" ] }, { "cell_type": "code", "execution_count": 297, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=6988, q=7907, décomposition en facteurs premiers de 6988 : [(2, 2), (1747, 1)]\n", "Symbole de Legendre (p|q) avec p=919, q=1747, décomposition en facteurs premiers de 919 : [(919, 1)]\n", "Symbole de Legendre (p|q) avec p=828, q=919, décomposition en facteurs premiers de 828 : [(2, 2), (3, 2), (23, 1)]\n", "Symbole de Legendre (p|q) avec p=22, q=23, décomposition en facteurs premiers de 22 : [(2, 1), (11, 1)]\n", "Symbole de Legendre (p|q) avec p=2, q=23, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Symbole de Legendre (p|q) avec p=1, q=11, décomposition en facteurs premiers de 1 : []\n", "Calcul en 6 appels\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 297, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(6988,7907, True)" ] }, { "cell_type": "code", "execution_count": 298, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7907 * 0 + 6988 = 3408 ** 2 = > False\n", "7907 * 2559 + 6988 = 4499 ** 2 = > True\n" ] }, { "data": { "text/plain": [ "[(0, 3408), (2559, 4499)]" ] }, "execution_count": 298, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resolution_residu_quadratique(6988,7907, debug = True)" ] }, { "cell_type": "code", "execution_count": 299, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=-1, q=5, décomposition en facteurs premiers de -1 : [(-1, 1)]\n", "Calcul en 1 appels\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 299, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(-1, 5, True)" ] }, { "cell_type": "code", "execution_count": 300, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5 * 0 + 4 = 2 ** 2 = > True\n", "5 * 1 + 4 = 3 ** 2 = > True\n" ] }, { "data": { "text/plain": [ "[(0, 2), (1, 3)]" ] }, "execution_count": 300, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resolution_residu_quadratique(-1, 5, debug = True)" ] }, { "cell_type": "code", "execution_count": 301, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=-1, q=7, décomposition en facteurs premiers de -1 : [(-1, 1)]\n", "Calcul en 1 appels\n" ] }, { "data": { "text/plain": [ "-1" ] }, "execution_count": 301, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(-1, 7, True)" ] }, { "cell_type": "code", "execution_count": 302, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 302, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resolution_residu_quadratique(-1, 7, debug = True)" ] }, { "cell_type": "code", "execution_count": 303, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=2, q=7, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Calcul en 1 appels\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 303, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(2, 7, True)" ] }, { "cell_type": "code", "execution_count": 304, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7 * 0 + 2 = 3 ** 2 = > False\n", "7 * 2 + 2 = 4 ** 2 = > True\n" ] }, { "data": { "text/plain": [ "[(0, 3), (2, 4)]" ] }, "execution_count": 304, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resolution_residu_quadratique(2, 7, debug = True)" ] }, { "cell_type": "code", "execution_count": 305, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symbole de Legendre (p|q) avec p=2, q=17, décomposition en facteurs premiers de 2 : [(2, 1)]\n", "Calcul en 1 appels\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 305, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symbole_legendre(2, 17, True)" ] }, { "cell_type": "code", "execution_count": 306, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "17 * 0 + 2 = 6 ** 2 = > False\n", "17 * 7 + 2 = 11 ** 2 = > True\n" ] }, { "data": { "text/plain": [ "[(0, 6), (7, 11)]" ] }, "execution_count": 306, "metadata": {}, "output_type": "execute_result" } ], "source": [ "resolution_residu_quadratique(2, 17, debug = True)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.8" } }, "nbformat": 4, "nbformat_minor": 4 }