Backup Newffr.com

Comment ça marche ?

Retour index
Retour forum

Factoriser des nombres. - Thread en ligne

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 ).
Code:

. 110
. x 11
. ------
. 110
. + 110
. ------
. 10010


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:
Code:

. a[2] a[1] a[0]
. x b[1] b[0]
. -----------------------------------------
. a[2]*b[0] a[1]*b[0] a[0]*b[0]
. + a[2]*b[1] a[1]*b[1] a[0]*b[1]
. ---------------------------------------------------
. r[4] r[3] r[2] r[1] r[0]


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 :

Code:

/********************
* Factorisation
* Algorithme naïf
*/

#include <stdio.h>


void init(int n) {
int j = 2;
int i = n;
while (i != 1) {

if ((i % j) == 0) {
i = i/j;
printf("%i ",j);
} else
j++;

}
printf("\\n");
}


int main(int argc, char **argv) {
int i;
for (i=1; i<argc;i++) {
printf("factorisatin de %sn",*(argv+i));
init(atoi(*(argv+i)));
}
return 0;
}




et voilà les résultats:

Code:

TboWan@Obelix:~/programmes$ time fac_naif 2147483647
factorisatin de 2147483647
2147483647

real 1m5.618s
user 1m4.810s
sys 0m0.030s
TboWan@Obelix:~/programmes$ time factorise 2147483647
factorisatin de 2147483647
( 2147483647 , 1 )
( 1 , 2147483647 )

real 0m0.058s
user 0m0.050s
sys 0m0.000s



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:
Code:

[n-0-x@localhost C]$ ./fdn.o time factorise 2147483647
2 2 2 2 2 2 (... [ c'est vraiment très long, ça prend toutes les lignes de mon terminal]) 2 2 2 22 2
[3]+ Stopped ./fdn.o time factorise 2147483647
[n-0-x@localhost C]$



Étrange...
[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) :

Code:

/********************
* Factorisation
* Algorithme naïf 2
*/

#include <stdio.h>

void init(int n) {
int i;

while (n%2==0) {printf("2 ");n=n/2;}

for(i=3;i*i<=n;i+=2)
if ((n % i) == 0) {
n = n/i;
printf("%i ",i);
}
printf("%i \n",n);
}

int main(int argc, char **argv) {
int i;

for (i=1; i<argc;i++) {
printf("factorisation de %s\n",*(argv+i));
init(atoi(*(argv+i)));
}
return 0;
}



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...

Code:

cas = a[0] - '0' + 2*(b[0]-'0') + 4*( (som + (nombre[rang] - '0')) % 2);

en fait que fait a[0] - '0' ?? ca ne soustrait pas 2 string ?
si cela fonctionne keske ca donne comme resultat ?



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 (j'sais pas pourquoi mais j'ai l'impression que je vais encore me faire banir d'un forum de cardeur moi)):

Code:
#include <stdio.h>

#include <gmp.h>

main(void)
{
unsigned long int exp;
mpz_t i,nb_bits,z,nombre,temp;
mpz_init(i);
mpz_init(nombre);
mpz_init(z);
mpz_init(nb_bits);
mpz_init(temp);
FILE *fichier;

mpz_set_ui(nombre,1);
fflush(stdout);

fichier=fopen("nb_premier.txt","a"); //fichier de sauvegarde

while(1)
{
mpz_set_ui(nb_bits,mpz_sizeinbase(nombre,2)); //on récupère le NB de bits
mpz_set_ui(z,1);
mpz_set_ui(i,0);
while(mpz_cmp(i,nb_bits)<0) //tant qu'on a pas testé tous les bits
{
mpz_mul(z,z,z);
mpz_mod(z,z,nombre);
mpz_sub(temp,nb_bits,i); //
exp=mpz_get_ui(temp)-1; //on teste si le bit
mpz_ui_pow_ui(temp,2,exp); //courant est à 1
mpz_and(temp,nombre,temp); //
if(mpz_cmp_ui(temp,0)!=0) //si il est à 1 alors
{
mpz_mod(z,z,nombre);
mpz_add(z,z,z);
}
mpz_add_ui(i,i,1); //incrémentation du compteur de boucle
}
if(mpz_cmp_ui(z,2)==0) //si le nb est premier, on le sauvegarde
{
mpz_out_str(fichier,10,nombre);
fprintf(fichier,"\n");
}
mpz_add_ui(nombre,nombre,2); //on teste le nombre suivant
}
}


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:

Code:
NOMBRE -> nombre à tester

I -> compteur de boucle
NB_BITS -> nombre de bits de NOMBRE
Z -> variable permettant de définir si NOMBRE est premier ou non

pour NOMBRE variant de 1 à l'infini
1 -> Z
nombre de bits de NOMBRE -> NB_BITS
pour I variant de NB_BITS-1 à 0
((Z * Z) % NOMBRE) -> Z
si (NOMBRE && 2^I) = 1 alors
((Z + Z) % NOMBRE) -> Z
fin si
fin pour
si (Z = 2) alors
sauvegarder NOMBRE
fin si
NOMBRE + 2 -> NOMBRE
fin pour

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...)

Code:

dim tableau(2 to 10001) as short 'parce qu'on peut pas faire plus grand

for i = 2 to 10001
tableau(i) = 1
next

dim i,j as integer
i=2

REM au début de chaque boucle, i est premier
while i<10001
j=i+i
REM on supprime les multiples de i
while j<10001
tableau(j) = 0
j = j+i
wend
i = i+1
REM on parcour jusqu'au prochain premier
while i<10001 and tableau(i) = 0
i = i+1
wend
wend

for i=2 to 10001
if tableau(i) = 1 then
print i ; " est premier"
end if
next



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
Quote:
C'est l'algorithme le plus rapide connu pour énumérer les nombres premiers



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 (d'ailleurs, en le relisant, je vois que je pourrai l'améliorer )
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)

Code:
/* factor -- print prime factors of n.

Copyright (C) 86, 1995-2002 Free Software Foundation, Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */

/* Written by Paul Rubin <phr@ocf.berkeley.edu>.
Adapted for GNU, fixed to factor UINT_MAX by Jim Meyering. */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>

#include <assert.h>
#define NDEBUG 1

#include "system.h"
#include "closeout.h"
#include "error.h"
#include "inttostr.h"
#include "long-options.h"
#include "readtokens.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "factor"

#define AUTHORS "Paul Rubin"

/* Token delimiters when reading from a file. */
#define DELIM "nt "

/* FIXME: if this program is ever modified to factor integers larger
than 2^128, this constant (and the algorithm will have to change. */
#define MAX_N_FACTORS 128

/* The trial divisor increment wheel. Use it to skip over divisors that
are composites of 2, 3, 5, 7, or 11. The part from WHEEL_START up to
WHEEL_END is reused periodically, while the "lead in" is used to test
for those primes and to jump onto the wheel. For more information, see
http://www.utm.edu/research/primes/glossary/WheelFactorization.html */

#include "wheel-size.h" /* For the definition of WHEEL_SIZE. */
static const unsigned int wheel_tab[] =
{
#include "wheel.h"
};

#define WHEEL_START (wheel_tab + WHEEL_SIZE)
#define WHEEL_END (wheel_tab + (sizeof wheel_tab / sizeof wheel_tab[0]))

/* The name this program was run with. */
char *program_name;

void
usage (int status)
{
if (status != 0)
fprintf (stderr, _("Try `%s --help' for more information.n"),
program_name);
else
{
printf (_("
Usage: %s [NUMBER]...n
or: %s OPTIONn
"),
program_name, program_name);
fputs (_("
Print the prime factors of each NUMBER.n
n
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("
n
Print the prime factors of all specified integer NUMBERs. If no argumentsn
are specified on the command line, they are read from standard input.n
"), stdout);
printf (_("nReport bugs to <%s>.n"), PACKAGE_BUGREPORT);
}
exit (status);
}

/* FIXME: comment */

static int
factor (uintmax_t n0, int max_n_factors, uintmax_t *factors)
{
register uintmax_t n = n0, d, q;
int n_factors = 0;
unsigned int const *w = wheel_tab;

if (n < 1)
return n_factors;

/* The exit condition in the following loop is correct because
any time it is tested one of these 3 conditions holds:
(1) d divides n
(2) n is prime
(3) n is composite but has no factors less than d.
If (1) or (2) obviously the right thing happens.
If (3), then since n is composite it is >= d^2. */

d = 2;
do
{
q = n / d;
while (n == q * d)
{
assert (n_factors < max_n_factors);
factors[n_factors++] = d;
n = q;
q = n / d;
}
d += *(w++);
if (w == WHEEL_END)
w = WHEEL_START;
}
while (d <= q);

if (n != 1 || n0 == 1)
{
assert (n_factors < max_n_factors);
factors[n_factors++] = n;
}

return n_factors;
}

/* FIXME: comment */

static int
print_factors (const char *s)
{
uintmax_t factors[MAX_N_FACTORS];
uintmax_t n;
int n_factors;
int i;
char buf[INT_BUFSIZE_BOUND (uintmax_t)];

if (xstrtoumax (s, NULL, 10, &n, "") != LONGINT_OK)
{
error (0, 0, _("`%s' is not a valid positive integer"), s);
return 1;
}
n_factors = factor (n, MAX_N_FACTORS, factors);
printf ("%s:", umaxtostr (n, buf));
for (i = 0; i < n_factors; i++)
printf (" %s", umaxtostr (factors[i], buf));
putchar ('n');
return 0;
}

static int
do_stdin (void)
{
int fail = 0;
token_buffer tokenbuffer;

init_tokenbuffer (&tokenbuffer);

for (;;)
{
long int token_length;

token_length = readtoken (stdin, DELIM, sizeof (DELIM) - 1,
&tokenbuffer);
if (token_length < 0)
break;
fail |= print_factors (tokenbuffer.buffer);
}
free (tokenbuffer.buffer);

return fail;
}

int
main (int argc, char **argv)
{
int fail;

program_name = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);

atexit (close_stdout);

parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
AUTHORS, usage);
/* The above handles --help and --version.
Since there is no other invocation of getopt, handle `--' here. */
if (argc > 1 && STREQ (argv[1], "--"))
{
--argc;
++argv;
}

fail = 0;
if (argc == 1)
fail = do_stdin ();
else
{
int i;
for (i = 1; i < argc; i++)
fail |= print_factors (argv[i]);
}
if (fail)
usage (EXIT_FAILURE);

exit (fail);
}



[ Ce Message a été édité par: Hindifarai le 2005-11-18 20:40 ]


Newffr.com 2001-2006