|
TboWan le 02/12/2004 06:58 |
Factorisation des nombres.
Nous savons tous multiplier des nombres, aussi grands soient-ils. C'est un problème relativement simple et les algorithmes le résolvent rapidement. La complexité des algorithmes de multiplication est de l'ordre de ln(n) (ou n est la taille des nombres à multiplier). Mais le problème inverse est plus dificile. Pour de petits nombres, c'est encore possible mentalement. Le problème vient du fait que nous ne disposont pas d'un algorithme qui calcule les factorisations mathématiquement, à partir d'une formule. On doit systmatiquement faire une recherche armis les nombres inférieurs. Les passer tous en revue pour savoir quelles sont les facteurs du nombre. Les recherches dans ce domaine portent toujours sur la réduction du nombre de nombres à tester. Mais il s'agit toujours de tests exhaustifs parmis un ensemble de nombres. Pourtant, ce n'est pas la seule solution. En effet, je vais vous présenter une idée originale (fruit de mon imagination) qui permet de factoriser les nombres, sans tests, sans division. Un autre point de vue du problème en quelque sorte. Cette idée met en oeuvre la multiplication écrite (comme en primaire) mais en binaire, un peut de logique (logique du premier ordre entre autres) et les parcours d'arbres. rassurez-vous, aucune connaissance spécifique n'est nécessaire pour comprendre la suite. L'idée part donc du calcul écrit en binaire. En fait, la forme de caclul écrit est une manière plus concrète et visuelle pour voire l'algorithme implanté pour les multiplications. Mais pour mieux comprendre le chmilblick, voici un petit exemple. Multiplions 6 ( 110 ) par 3 ( 11 ).
ce qui nous donne 18. D'une manière plus générale, on peut remplacer les chiffres des opérande de la multiplication par des variables et on obtien une version plus générale de la méthode:
Et plus formellement, on aurait les formules du genre: Pour les sommes de colones: somme[i] = somme(k=0..i) { a[k] * b[i-k] } Pour la propagation de la retenue: retenue[0] = 0 retenue[i] = ( somme[i] + retenue[i-1] ) / 2 Pour la valeur des chiffres du résultat: r[i] = ( somme[i] + retenue[i-1] ) modulo 2 Pour factoriser un nombre, la première approche consiste à écrire ces équations pour chaque i en remplacant les r[i] par le bon chiffre de notre ombre à factoriser et de générer un système (qui les regroupe). On peut remarquer qu'on va devoir considérer des r[i] pour i supérieur à l longueur du nombre... on les posent comme étant à '0', un nombre à donc une écriture infinie, du fait qu'on complète l'écriture par des 0. Mais pour ça, on peut simplifier certaines choses. Les facteurs auront une écriture forcément plus courte que le nombre à factoriser (sinon, il y a un ch'ti problème). Et en lesx multipliants, on peut pas dépasser une certaine longueur. Donc, on a pas besoin de considérer une suite infinie de '0' mais on peut tronquer pour une certaine longueur. J'introduis ici la notion de poid d'un chiffre, c'est en fait son numéro de place dans l'écriture du nombre. Et le poid d'un nombre, c'est le maximum des poids des '1' du nombre... ou encore, le poid du '1' le plus à gauche. De cette manière, on peut introduire la contrainte suivante: la somme des poids des facteurs ne peut pas exéder le poid du nombre. Cette contrainte est facile à comprendre... les poids des facteurs déterminent une minoration de la taille de leur produit. On pourrait s'atarder à montrer que ce problème est décidable. Et qu'il existe un algorithme pour le résoudre. Cette démonstration se base sur le fait que les variables en présence on un espace de valeur finis ('0' ou '1'). Mais je ne m'y attarderai pas car cela n'apporte rien au raisonement. Nous nous retrouvons donc avec un système d'équations à résoudre. La première approche est celle du 'brute-force', on essaie toutes les possibilités. Mais c'est équivalent à essayer tous les nombres et voire si ils divisent le notre. Autant garder les algorithmes classiques dans ce cas. la deuxième, c'est d'utiliser un solver. Comme je l'ai dit, le problème est décidable, on peut donc faire confiance au algorithmes disponibles pour résoudres des systèmes de ce genre, ils termineront. Un première version du programme (que je fournis pas car elle inutile) pourrait se faire en Maple. On crée le système d'équation, on appelle la fonction solve() de Maple et il nous trouve les solutions. Après l'interpretation des résultats, on obtien les facteurs du nombre. Comme on s'en doute, ce genre de programme ne sera pas efficace. La raison est simple, la résolution de systèmes d'équation (sans considération de la forme du système, du type de données,...) met en oeuvre beaucoup de calcul. C'est donc là que réside notre point faible. Et c'est ce que nous allons améliorer... Une technique empreintée à la logique consiste à développer un arbre à partir du système d'équations. En logique, on développerait l'arbre en tirant des conclusions d'après les équations... ei on a par exemple: "a et b = vrai" on en déduit que a doit être vrai et que b aussi. On ajoute deux "équations": l'équation "a = vrai" et l'équation "b = vrai". Quand on tombe sur une équation du genre "a ou b = vrai", on ajoute deux successeurs, l'un pour l'équation "a = vrai", l'autre pour "b = vrai". Si on obtient des contradiction dans une branche, on l'élimine (par exemple, un "a = vrai" puir, autre part dans la branche, "a = faux"). On arrète de développer l'arbre quand il n'y a plus d'équation à développer. Mais dans notre cas, nous devons prendre en compte des entiers (pour la retenue). Cette technique n'est donc pas appliquable directement. Mais c'est sur cette base que je suis parti pour élaborer mon algorithme. Dans notre cas, au lieu de stocker des équations, nous allons retenir les valeur des chiffres que nous devinons et le poids des nombres que nous sommes en train de construire. Pour faciliter encore les calculs, nous déterminerons les chiffres dans l'ordre de poids croissants. On construit donc les facteurs en les écrivant petit à petit. La recherche de facteur correspond donc à creer l'arbre de dérivation du système d'équations de départ. Vu qu'on ajoute les chiffres dans l'ordre, à chaque "étape", on peut connaître la retenue correspondante. Pour ensuite, faciliter les déductions. L'algorithme de recherche de facteurs est donc un parcours d'arbre. Ce parcours sera fait en profondeur-d'abord pour éviter de surcharger la mémoire. En gros, je vais utiliser uen fonction récursive pour elaborer l'arbre. Les adeptes de languages récursif ne seront donc pas dépaysé. A une étape, nous connaissons donc le début de deux facteurs, la retenue qu'il faudra prendre en compte et le chiffre du nombre correspondant à notre étape. Avec cela, nous avons largement assez. Tout d'abord, il faut calculer la somme (comme dans les équations au début). Mais attention, il ne faut pas prendre en compte le premier chiffre de chaqu'un des facteurs (puisqu'il devrait être multiplié par des inconnues). On y ajoute la retenue. A ce stade, il faut séparer le blanc du jaune. D'un coté, la partie de cette somme vraiment utile pour déterminer les chiffres à ajouter, de l'autre, la future retenue (modulo 2 pour le premier, / 2 pour le second). En fonction du premier chiffre de chaque facteur et de la différence entre le chiffre du nombre à obtenir et notre somme, nous pouvons déterminer facilement les possibilités qui s'ouvrent à nous. Voici quelques exemple pour éclairer dans ce brouilard (je note entre parenthèses, le premier chiffre de a, celui de b et la différence entre la somme et le chiffre désiré): ( 0, 0, 0) : on peut ajouter n'importe quelle compinaisons devant nos facteurs, la somme étant bonne, aucun problème. ( 1, 0, 0) : cette fois, on ne peut ajouter qu'un '0' au deuxième facteur (pour le premier facteur, aucune restriction). Car sinon, notre somme augmenterais de 1 et on ne tomberai pas sur le nombre désiré. (1, 0, 1) : là, c'est presque l'inverse: on doit mettre un 1 en tête du deuxième facteur mais ce qu'on veux pour le premier. (0, 0, 1) : là, c'est inutile de continuer car il n'y a pas de solution. (1, 1, 0) : Soit on et un 0 devant chaque facteur, soit un 1 devant qhaqu'un des deux. Dans ce dernier cas, il faut mettre à jour la retenue car celle-ci doit augmenter de 1. L'ajout des chiffres peut se faire dans une fonction à part qui ferait les vérification de poids (pour éviter d'avoir des nombres trops grands). Le programme s'exécute donc par l'initialisation: on établis les premiers chiffres en les ajoutant à des facteurs vides. L'ajout fait ensuite appelle à une une autre fonction faisant les calculs pour déterminer les chiffres à ajouter. Pour chaque possibilité, cette fonction rappelle la fonction d'ajout, d'où le mécanisme récursif. Quand aucune solution ne peut être trouvée, les fonctions se terminent tout simplement, et si on est arrivé à la fin du nombre, c'est qu'on a trouvé une factorisation possible et on l'affiche (par exemple). Un exemple en C d'un tel programme est disponible à l'adresse suivante: http://www.lifl.fr/~henin/programmes/factorise.c Pour la complexité, en posant n, le nombre à factoriser, et sans vouloir être précis, on peut voire que chaque noeud requière un parcours de tableau proportionnel à sa profondeur dans l'arbre. La complexité de l'algorithme est donc (après calcu) de l'ordre de n * log(n). Une amélioration qui consisterai à implémenter le calcul de la somme en hardware réduirait la complexité vers n. Qui deviendrait une majoration. Ce qui est important si on sait que les algorithmes actuels de factorisation sont de l'ordre de n. En guise de conclusion, si on peut améliorer le calcul de la somme, on disposera d'un algorithme de complexité plus faible encore que ceux disponibles. La factorisation devenant plus simple, les algorithmes de cryptographie pourront alors être mis à mal plus facilement. TboWan [ Ce Message a été édité par: TboWan le 2004-12-02 07:02 ] |
||||||
|
TboWan le 02/12/2004 19:04 |
Une précision...
Parce qu'il faut rendre ce que de droit, et laisser une faute, c'est inadmissible. Les algorithmes actuels ne sont pas en complexité linéaire (n) comme je l'ai dis mais certians sont en sqrt(n)... De plus, après quelques calculs, j'ai vu que mon algorithme peut être majoré par n mais il est surtout minoré par sqrt(n). Il ne peut donc supplanter les autres, RSA à eu chaud mais rien n'a changé. |
||||||
|
Goddy le 03/12/2004 00:47 |
L'option -v est très sympa, bien utile aussi... bon pour RSA t'y a été un peu fort [ Ce Message a été édité par: Goddy le 2004-12-03 00:47 ] |
||||||
|
marin_shadok le 05/12/2004 15:44 |
interessant ... je me pencherai dessus plus en profondeur des que j'aurais un peu de temps ... | ||||||
|
TboWan le 08/12/2004 03:53 |
J'ai fait un test de comparaison avec un algorithme naïf suivant :
et voilà les résultats:
No Comment [ Ce Message a été édité par: TboWan le 2004-12-08 03:57 ] |
||||||
|
N-0-X le 08/12/2004 19:32 |
Euh, t'es sur que c'est le bon code source? Car chez moi:
[addsig] [ Ce Message a été édité par: Zamer le 2005-11-19 01:09 ] |
||||||
|
o.O le 08/12/2004 20:00 |
Je propose des améliorations à ton algo naif (on divise que par les entiers inf à la racine de n et par les nombres impairs) :
Voilà maintenant on peut comparer... |
||||||
|
TboWan le 08/12/2004 20:33 |
avec le nouvel algo naïf:
TboWan@Obelix:~/programmes$ time fac_2 2147483647 factorisation de 2147483647 2147483647 1 real 0m38.572s user 0m33.820s sys 0m0.020s N-0-X, C'est le code que j'ai utilisé chez moi ... donc, à priori, c'est le bon. L'important, de toute façon, c'est pas le code, c'est l'algo (et dans notre cas, le fait qu'il soit moins rapide) |
||||||
|
marin_shadok le 26/12/2004 15:20 |
est ce que ya moyen de modeliser ton algo par un automate ? | ||||||
|
TboWan le 26/12/2004 21:48 |
Pour l'automate, j'y ai pas encore pensé...
Tout d'abord, si c'est possible, je pense à un automate non-déterministe. Et en plus, je pense que l'automate devrait être différent pour chaque nombre à factoriser. Mais c'est l'intuition du moment, je vais y réfléchir, et quand je trouverai la réponse, je vous la donnerai Patientez donc |
||||||
|
gogo le 26/12/2004 22:31 |
à propos de programmation et de mathématiques, je ne peux pas m'empêcher de faire ma petite pub pour la plus géniale des librairies : NTL.
Cette librairie permet de manipuler des très grands nombres (de plus d'un million de chiffres par exemple) avec aisance et rapidité. C'est très utile pour la recherche de nombres premiers, la factorisation, la cryptographie, etc. Avec les fonctions de base en C/C++ ce genre de travail est presque impossible et, surtout, ça donne des trucs super lents... Je vous file le lien : www.shoup.net/ntl L'auteur de cette librairie s'appelle Victor Shoup (un grand homme!). Dans le même style de librairie, vous avez GMP (http://www.swox.com/gmp/) qui est destinée d'abord aux systèmes Unix... l'install sous Windows est assez complexe. C'est plus ou moins aussi rapide que NTL mais c'est plus complet (paraît-il) et plus répandu. Enfin, un lien qui peut toujours servir : http://www.haypocalc.com/lien/ "Des maths? Oui, mais des Manzani!" [ Ce Message a été édité par: gogo le 2004-12-26 22:32 ] |
||||||
|
TboWan le 25/01/2005 18:19 |
Je revient donc des méandres où je voguais librement pour vous faire part de mes conclusions...
Pour l\'automate, c\'est DTC (pour ne pas être vulgaire). Parce que l\'automate ne véhicule pas vraiment de données entre ses états. Ou alors, c\'est un automate à pile. Dans ce cas, la pile contient des constantes dont le nombre est fixé (un type énuméré en quelque sortes). Donc, pas question d\'y mettre des entiers (celui qui me dit \"les entiers vont de -2^machin à 2^truc, ils sont donc bornés, je ne lui répondrai même pas). En plus, il nous faut deux données à véhiculer entre chaque transitions (les deux ébauches de nombres et les deux retenues). Or, on ne considère que le somme de la pile. C\'est donc en dehors des spécifications des automates. Maintenant, celui qui me dit \"machine de Turing\", et bien RTFM. En effet, elles sont équivalentes à nos bonnes vielles architectures de Von Neumann (nos x86 et autres loufoqueries). Donc, c\'est raté pour les automates. Mais rien ne nous empêche de représenter le déroulement du programme par un graphe d\'états (purement formal et graphique donc). TboWan |
||||||
|
TboWan le 07/03/2005 16:14 |
Petites précisions qui m'ont été demandées...
Premier indice ... "code ascii" Deuxième indice ... "arithmétique sur les caractères" Troisième indice ... "on veut un 0 si c'est '0', un 1 si c'est '1'" Sinon, faut consulter (google ou un psy, c'est comme on veut). |
||||||
|
TboWan le 15/11/2005 19:39 |
Ca fait un bail ...
J'ai fait quelques stats sur la complexité du chmilblick, c'est pas joli... pour factoriser n, on est en o(racine_caree(n)), bref, l'algo naif optimisé On peut cependant faire mieux, mais c'est pas pour autant gagné... Par contre, si on pouvait paralléliser le truc, ce serait mieux (mais faudrait la patates de µp)... affaire à suivre (enfin, j'espère) |
||||||
|
casskroot le 15/11/2005 23:44 |
J'vous donne un petit source que j'ai codé (merci à nirvok pour la méthode de calcul
Le prog utilise la librairie GMP et est assez rapide mais bon si c'est pour casser du RSA, c'est toujours pas la peine d'y penser. J'vous donne un petit algo en prime:
|
||||||
|
N-0-X le 16/11/2005 19:39 |
Pour les nombrespremiers, j'ai codé un petit truc en Python qui utilise le crible d'Erathosthène (peut-être est-ce que je me trompe dans l'orthographe).
Le but est de faire une liste (ou un array pour ce qui n'utilisent pas le python) qui ocntient tous les nombres de 2 à N. Puis, on vérifie si un nombre est premier en le divisant (euclidiennement)par un nombre précédent et en regardant si le reste est nul. Si le reste n'est pas nul, alors c'est un nombre premier. Si c'est un nombre premier, alors on retire tous ses multiples de la liste. Un fois fini, il ne nous reste plus qu'à afficher la liste [addsig] [ Ce Message a été édité par: N-0-X le 2005-11-16 19:40 ] |
||||||
|
casskroot le 16/11/2005 19:51 |
le pb avec cette méthode c'est que dès que tu travaille avec des grands nombres, il faut des Go ou meme des To de ram ou alors mettre en cache sur le disque dur, ce qui est très long. | ||||||
|
TboWan le 17/11/2005 18:24 |
Le crible d'Eratosthène... (en Basic parce que c'est bon...)
Voilà, voilà. C'est l'algorithme le plus rapide connu pour énumérer les nombres premiers. Par contre, c'est vrai qu'il nécessite un sacré paquet de mémoire (donc, on le code pas en Basic [ Ce Message a été édité par: TboWan le 2005-11-17 18:25 ] |
||||||
|
casskroot le 17/11/2005 19:01 |
Ben pour les tres tres petits nombres premiers alors parce que va me trouver le premier nombre premier qui se trouve après 1 000 000 000 000 000 avec ton prog et avec le mien et tu verras la différence |
||||||
|
N-0-X le 17/11/2005 21:33 |
Concernant le crible d'Erathothène, deux mathématicien ont [i)réussis[/i] à l'améliorer en ajoutant un système d'équtations à plusieurs inconnues. Le but est ensuite de retirer de la liste les solutions de ces équations pas franchement facile... lol (j'avais vu ça sur Wikipedia Sinon, pour le crible d'Erathostène en Python, je ne vais as poster tout le code ici, mais si ça intéresse quelqu'un, je vous le passerai volontier |
||||||
|
TboWan le 18/11/2005 09:37 |
(très vite parce que je dois y aller)...
dans l'algo de casskroot, tu teste tous les nombres (par exemple, tu passe sur tous les pairs), alors que le miens passe son temps à les éliminer (on ne traite qu'un seul pair). (C'est l'idée générale du moins) |
||||||
|
casskroot le 18/11/2005 18:11 |
Oui je connais le principe d'eratosthene mais je t'assure que c'est rapide uniquement sur des petits nombres (enfin là t'es en train de me mettre le doute dans l'ame, je sais plus si j'avais laissé tomber cette méthode uniquement à cause de la mémoire que ca necessite ou aussi à cause de la rapidité). | ||||||
|
Hindifarai le 18/11/2005 20:38 |
**edit** le code étant quasi illisible -> ftp://alpha.gnu.org/gnu/coreutils/
vous y trouverez le package contenant factor() Hop hop une url importante : http://primes.utm.edu/ et le code de factor() du kernel GNULinux : (contenu dans le package coreutils pour les curieux et si jamais la version date)
[ Ce Message a été édité par: Hindifarai le 2005-11-18 20:40 ] |