begin process at 2013 05 22 04:03:24
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Maths

 > TROUVER LES DIVISEURS D'UN NOMBRE ENTIER

TROUVER LES DIVISEURS D'UN NOMBRE ENTIER


 Information sur la source

Note :
10 / 10 - par 1 personne
10,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :Maths Classé sous :division diviseur, modulo nombre, trouver chercher, calculer performance, optimiser optimisation Niveau :Débutant Date de création :21/01/2008 Date de mise à jour :29/01/2008 06:17:36 Vu / téléchargé :32 022 / 837

Auteur : f0xi

Ecrire un message privé
Site perso
Commentaire sur cette source (25)
Ajouter un commentaire et/ou une note

 Description

Cliquez pour voir la capture en taille normale
suite a ce topic :

http://www.delphifr.com/infomsg_COMMENT-TROUV ER-DIVISEUR-NOMBRE-ENTIER_1063338.aspx#3

Voici un petit programme pour trouver TOUT les diviseurs d'un nombre entier (1 et le nombre lui même inclus).

Ce programme mets en pratique une fonctions totalement optimisé (aprés on attaque l'assembleur)
qui permet de trouver les diviseurs trés rapidement ... trop ?

indications sur la fenetre du prog :

Cycles : nombres de cycles CPU ecoulés entre le debut et la fin de la fonction
Temps  : temps en millisecondes ecoulés entre le debut et la fun de la fonction
Diviseurs : nombre de diviseurs trouvés (ou signalement d'un nombre premier pour la fonction optimisée)

utilisation :

choisissez un nombre entre 1 et 2 147 483 648
puis cliquez sur le bouton "give me all!" pour lancer le calcul, partez pas! c'est deja finit :)


 Conclusion

Inutile donc indispensable.


 Fichier Zip

Les Membres Club peuvent télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip


 Historique

21 janvier 2008 03:59:52 :
Erreur de manip (-.-)
28 janvier 2008 22:55:58 :
Refact commentaires, renomage de la fonction Caca -> Basique
29 janvier 2008 06:17:36 :
suppression de la fonction "basique", amélioration de l'algorithme de GetDivisors (merci a tous en particulier whitehippo, cirec et brunews), refact complet de la source.

 Sources du même auteur

Source avec Zip FRACTIONS, ADDITION, SOUSTRACTION, MULTIPLICATION, DIVISION ...
Source avec Zip CSV, CSVLIST, TSTRINGLIST
Source avec Zip COMMANDS MANAGER - BESOIN DE COMMANDES DANS VOS PROGRAMMES T...
Source avec Zip DYNAMIC LIBRARY LOADER CLASS: GAGNEZ DU TEMPS POUR CHARGER L...
Source avec Zip BASE64/BASE64URL ENCODE/DECODE

 Sources de la même categorie

Source avec Zip FRACTIONS, ADDITION, SOUSTRACTION, MULTIPLICATION, DIVISION ... par f0xi
Source avec Zip Source avec une capture MANIPULATION TRÉS RAPIDE DE TRÉS GRANDES NOMBRES ENTIERS + F... par kamel78
Source avec Zip CONVERSION OF (HEX BIN OCT) TO EACH OTHER par MSBMW
Source avec Zip Source avec une capture RESOLUTION EQUATIONS DEGRE "N" + CALCULETTE SCIENTIFIQUE par pseudo3
Source avec Zip Source avec une capture DEUX BIBLIOTHÈQUES POUR CALCULER AVEC DES ENTIERS TRÈS GRAND... par Rekin85

Commentaires et avis

Commentaire de f0xi le 21/01/2008 04:08:25 administrateur CS

J'ai oublier de preciser qu'on peu renomer le fichier fmd.ex_ en fmd.exe pour ceux qui desire avoir la version compilée (non delphiistes).


Commentaire de MAURICIO le 21/01/2008 15:14:56 administrateur CS 10/10

Tiens, une source de notre renard préferé,  ok elle est pourrie ...

En tout cas ça fait logntemps que l' on a pas de tes nouvelles!

Alors oui, la 2eme fonction est légèrement plus rapide, mais léger ... lol
Je mets 10/10 pour t' encourager à améliorer encore cela :)

A+

Commentaire de Debiars le 26/01/2008 10:40:06

J'aurais aimé avoir quelques commentaires m'expliquant le pourquoi et le comment de la vitesse d'exécution de la fonction optimisée.
Après force recherches, je vous livre le résultat de mes investigations.
Pour faciliter, appelons A la fonction "simpliste" telle que j'aurais pû l'écrire moi-même et B la fonction sophistiquée et optimisée à la Foxi.
Au premier rabord, on pourrait penser que B met deux fois moins de temps que A, vu que l'on divise le nombre testé par deux, en stockant à la fois le diviseur et le quotient,ce qui nous donnerait 26168 ms pour A et 13410 ms pour B. Que nenni, B met 0 ms. Diantre, me dis-je.
En faisant tourner à vide les deux boucles while, j'arrive effectivement à ce résultat. J'en déduis donc que ça se passe à l'intérieur de la boucle. C'est alors que je découvre le petit "break" exécuté quand le diviseur est plus grand que le quotient.
En examinant le résultat de l'opération (1152 chiffres), on constate que le nombre de boucles entre deux chiffres pour trouver la première moitié des diviseurs, varie entre 1 et 318, contre 318 et 432432000 pour la deuxième moitié.
Conclusion : la fonction B effectue 29568 boucles contre 86486400 pour la fonction A. Génial.
Néanmoins, mon cher Foxi, tu aurais pû supprimer ceci :

if (NPrime and $1) = 1 then
    PassMax := (NPrime+1) div 2
  else
    PassMax := NPrime div 2;
    
et remplacer  
     while DivisorA <= PassMax do
par while DivisorA <=  NPrime do

ce qui ne change rien au résultat puisque de toute façon tu breakes avant d'arriver à PassMax, et cela aurait évité de m'embrouiller la comprenette.

Commentaire de cruchacode le 28/01/2008 11:16:43

Gestion de la mémoire : tu peux agrandir les tableaux par incrément de 10... et redimensionner en sortie

Commentaire de MAURICIO le 28/01/2008 11:23:03 administrateur CS

Je suis tout à fait d' accord avec Debiars: ça manque cruellement d' explications!

Commentaire de WhiteHippo le 28/01/2008 22:16:38

La première méthode est basique soit mais quand même....
De légères modifications peuvent grandement l'améliorer, même si cela ne la rend pas optimale :

function GetIntDivider2(const Number: integer): TIntArray;
var i : Integer;
begin
  i := 1;
  while i <= (Number div i) do
  begin
    if (Number Mod i) = 0 then
    begin
      SetLength(Result, Length(Result)+2);
      Result[High(Result)]   := i ;
      Result[High(Result)-1] := Number div i ;
    end;
    Inc(i);
  end;
end;

Cordialement.

Commentaire de f0xi le 28/01/2008 23:03:48 administrateur CS

while i <= (Number div i) do << ajoute une division a chaque iteration!

Result[High(Result)]   := i ;
Result[High(Result)-1] := Number div i ; << et le control des doublons ?!


j'ai ajouter des commentaires detaillés ...

Commentaire de WhiteHippo le 28/01/2008 23:15:29

"ajoute une division a chaque iteration!"
Ok, mais regarde le nombre de cycles gagnés en contrepartie.

"et le control des doublons ?!"
Des doublons ??? ou ça ???

Cordialement.

Commentaire de f0xi le 28/01/2008 23:22:24 administrateur CS

@debiars :

tu aurais pû supprimer ceci :

if (NPrime and $1) = 1 then
    PassMax := (NPrime+1) div 2
  else
    PassMax := NPrime div 2;
    
et remplacer  
     while DivisorA <= PassMax do
par while DivisorA <=  NPrime do


non non mon amis! comme expliquer dans la source,
la definition de PassMax permet d'avoir une vraie limite calculée par rapport a si le nombre est paire ou impair (+1) dans un premier lieux,
puis fixe une limite certaine dans le cas par exemple des nombres premiers qui ne declanches pas le break conditionné par DivisorA < DivisorB ou DivisorA = DivisorB
puisque aprés la decouverte de 1 par NPrime mod DivisorA puis de NPrime par NPrime div DivisorA, les conditions sont toutes ignorée et c'est la ou il reste encore une petite optimisation possible a faire.

dans le cas des nombres premiers la condition de boucle while DivisorA <= NPrime do
ferait donc en sorte d'etre itérée NPrime fois au lieux de PassMax fois (NPrime/2)
ce qui rendrais la fonction aussi lente que la version basique.

c'est la ou ma fonction optimisée a une faille, c'est qu'en cas d'un nombre premier NPrime n'ayant que pour diviseur 1 et NPrime lui meme, on doit quand même essayer jusqu'a NPrime/2 comme valeur de DivisorA, ce qui est un peu mieux que la basique mais pas encore parfait.

il nous faudrait donc ajouter une condition if DivisorA > DivisorB then break; avant la condition if (NPrime mod DivisorA) = 0 then ...
mais cela revient a ajouter une division (div) puis un saut conditionnel (cmp,  jl) a chaque iteration de la boucle ce qui pourrait penaliser la fonction sur les nombres non premier (donc les plus frequents).
d'ou le pourquoi du non ajout de cette condition, bien qu'au vus des performances globale ce ne serait pas forcement TRES penalisant.

voila, aprés cette lourde explication qui donne mal au crane, je vous remercie pour vos commentaires :)

Commentaire de WhiteHippo le 28/01/2008 23:31:08

"ce qui rendrais la fonction aussi lente que la version basique." voir même beaucoup plus lente dans le cas de la version basique améliorée.

Essaye avec la version que j'ai fourni et la optimized sur un nombre comme 42949483 par exemple, histoire de voir ;P

Cordialement.

Commentaire de f0xi le 28/01/2008 23:34:45 administrateur CS

@whitehippo :

je devrais la fermer avant d'avoir tester!
ta fonction est trés trés bien sauf pour certains chiffre (doublons de diviseurs)
qui peut etre résolus par une petite condition :
DA < DB /// DA = DB
:)

par contre tu m'a fait trouver un truc qui permet d'ameliorer encore la mienne :)

Commentaire de WhiteHippo le 28/01/2008 23:42:10

J'insiste, quels doublons !!!!! Il n'y a pas de doublons !!!!

P.S. pour une valeur comme 994917094, nombre non premier, j'obtiens à priori de meilleure résultats qu'avec la méthode optimized.

Cordialement.

Commentaire de f0xi le 28/01/2008 23:53:59 administrateur CS

essaye avec : 1, 4, 9, 16, 25, 49 et surrement d'autres :)

je vais mettre a jours la source avec une petite modif que je te doit :)

Commentaire de WhiteHippo le 29/01/2008 00:10:38

Je les voyais pas ses satanés doublons :)

Voici une version modifiée, et comme tu n'aimes pas les divisions, basculement vers les multiplications :

function GetIntDivider3(const Number: integer): TIntArray;
var i,i2 : Integer;
begin
  i := 1;
  repeat
    i2 := i*i ;
    if i2 > Number then break ;
    if (Number Mod i) = 0 then
    begin
      SetLength(Result, Length(Result)+1);
      Result[High(Result)] := i ;
      if Number<>i2 then
      begin
        SetLength(Result, Length(Result)+1);
        Result[High(Result)] := Number div i ;
      end ;
    end;
    Inc(i);
  until false ;
end;

Cordialement.

Commentaire de f0xi le 29/01/2008 06:26:15 administrateur CS

J'ai battus le score de BruNews!

pour 864 864 000 :
1657 cycles (version actuelle de l'algo)
1769 cycles (version C de brunews)

mais bon connaissant le bonhomme, il vas me pondre un truc version pur Asm qui dechire tout par calcul simplifié des parallélisations quantique du temps courbe via la simple ligne de code :
pqkkmovtsqu qppm0, [qppm1+$2A];

(°.o)

hihihi

Commentaire de WhiteHippo le 29/01/2008 12:34:29

@foxi

Pourrais tu faire des tests avec le code ci-dessous sur ta machine, car sur la mienne j'ai l'impression que cette méthode me donne des temps plus rapide par rapport à l'autre :

procedure GetDivisors(const Number: Longint; const pDivisors: pLongintArray;
          out DivisorsCount: Longint; out AsPrime: boolean);
var
  i,i2 : Integer;
  Divisor, Modulo : Longint;
begin
  DivisorsCount := 0 ;
  i := 1;
  repeat
    i2 := i*i ;
    if i2 > Number then break ;
    IDivMod(Number, i, Divisor, Modulo);
    if Modulo = 0 then
    begin
      pDivisors^[DivisorsCount] := i;
      DivisorsCount := DivisorsCount + 1;
      if Number<>i2 then
      begin
        pDivisors^[DivisorsCount] := Divisor;
        DivisorsCount := DivisorsCount + 1;
      end ;
    end;
    Inc(i);
  until false ;
  AsPrime := (DivisorsCount = 2) and ((pDivisors^[0] = 1) and (pDivisors^[1] = Number));
end;

Cordialement.

Commentaire de cirec le 29/01/2008 13:28:53 administrateur CS

@WhiteHippo :
je venais exactement pour le même type de remarque !

Sauf que chez moi ta procédure donne quasiment les mêmes résultats que le dernière de F0xi !!!

Et justement je voulais signaler que chez moi la toute première version
optimisée de F0xi reste la procédure la plus rapide (moins de cycles)
et que même GetIntDivider3 (de WhiteHippo) donne des résultats parfois en dessous de ceux de te dernière mouture ?

On en déduis donc que : arrivé à un certain niveau d'optimisation
c'est le processeur qui fera la différence entre des résultats plus performant sur les nouvelles machines (Normal) mais en revanche, sur des
machines plus anciennes ce même code peut donner des résultats moins bon qu'un code moins optimisé.

Sur AMD Athlon XP 1800+ 1.53 Ghz  512 Ram  (c'est pas un cheval de course ;) )

Belle démonstration d'optimisation que vous nous offrez ici
Bravo tous les deux

Commentaire de f0xi le 29/01/2008 20:52:23 administrateur CS

@WhiteHippo :

Un chouilla, un chouilla, a peine 5 a 10 cycles en moins...
je pense que cela viens de la multiplication et peut etre du repeat until.
la faudrait voir ce que Delphi nous pond comme assembleur sur une while et sur un repeat.
y'aura même peut etre des differences avec des goto et des label ...

mais on est tout les deux dans des performances identiques, de 0 a 10 cycles prés a peine.

@Cirec :

Je suis content que tu te montre enfin, je croyais que tu me faisais la gueule suite a notre petit "malentendus", donc j'en profite pour m'excuser d'avoir pus etre blessant si ça été le cas :)
potes ?



pour ce qui est des performances, la il est certains qu'ils faut prendre 2 choses en comptes :
La frequences du processeur (plus elle est élévées plus on a de cycles/secondes)
Le cache processeur (plus ou moins veloce il ralentira ou non la fonction)
La ram (plus ou moins rapide pour le transfer des données)

L'utilisation d'un tableau fixe permet de ne pas faire de redim en cours de route (ce qui prend enormement de temps) comme me la signaler Brunews. c'est en partie ce qui nous fait gagner le plus de temps (quasiment le double avec un tableau dynamique).

Pour depasser la limite (4000) de diviseurs il faudrait taper dans le int64 afin d'avoir un nombre qui en possede autant.
La aussi Brunews m'a expliquer que Windows alloue les pages memoires par 4Ko (4096 octets) donc un tableau de 4000 longint nous donne 4 pages memoires soit 16Ko au total (4000*4, 1000 par pages).
Bon brunews l'expliquerais mieux que moi.

Ensuite viens finalement les diverses regles d'ecriture de l'algorithme, qui permettent de limiter le nombre de boucle (divmod) de sortir a temps (les deux condition while ou repeat et break).

Bref il est certains que nous sommes en presence d'un bel exercice d'optimisation permettant de constater comment une methode se comporte quand on l'ecrit de diverse maniere.





Commentaire de cirec le 30/01/2008 02:11:49 administrateur CS

@F0xi :
vu ton inquiétude je suppose que tu n'as pas lu mon message sur le forum ...
je te rassure je ne fais pas la gueule ... et c'est bizarre parce que je pensais la même chose de toi puisque tu n'avais pas répondu au message ... (du forum)

"Potes ?" bien sur que oui, le contraire ne m'a même pas effleuré ^^

Commentaire de Albedo039 le 31/01/2008 15:34:48

Heuuuu... juste un petit mot en passant ;)
Si le nombre à étudier est impair, alors on peut optimiser encore.

En effet, on peut tester en "sautant" les diviseurs potentiels qui sont pairs, puisque les nombres pairs ne "génèrent" (à la multiplication) que des nombres pairs.
Dans ce cas
DivisorA := DivisorA + 2;
fait l'affaire.

Statistiquement parlant, c'est un gain de 25% (50% de tests en moins pour 50% des nombres à étudier possibles...)

Commentaire de Albedo039 le 31/01/2008 16:25:58

On peut opérer les tests de "haut en bas" (du plus grand au plus petit diviseur possible)
puisqu'on sait déjà que le plus grand diviseur possible est le nombre en entrée divisé par 2.

Si ce premier "diviseur à tester" est pair, alors après l'avoir tester, on peut le diviser par 2 de nouveau: c'est le prochain "diviseur possible".... et ainsi de suite.

Faire le test p.e. avec 2717908992: 81 x 2 x 2 x... (25 fois 'par2')
on pourrait alors optimiser cette partie du code:
- chercher les combinaisons possibles des 25 fois 'par2' (2, 4, 8, 16, 32... 33554432)
- le résultat de la dernière division par 2 est 81. calculer les combinaisons de ce nombre avec les combis précédentes.
- étudier "81" pour voir s'il est lui-même "divisible" : s'il est divisible, alors les diviseurs peuvent être "combinés" avec les nombres précédents...
Par exemple ici:
81 = 9x9
81 = 3x3x3x3
81 = 27x3
on sait donc que 3, 9 et 27 sont des diviseurs
mais donc aussi que
3x2
3x4
3x8
3x16
...
3x33554432
et ainsi de suite avec 9 et 27.

++++++++++++++++++++++++++++++++++
Je pense que l'on peut optimiser beaucoup plus encore :)

Par exemple en utilisant des embranchements de code:

if JeSuisPair then
TrouveDiviseursProc:= Ma_Proc_Speciale_Nombres_Pairs
else
TrouveDiviseursProc:= Ma_Proc_Speciale_Nombres_Impairs;
TrouveDiviseursProc(...);

et bien sûr qqc comme:

procedure Ma_Proc_Speciale_Nombres_Pairs(...);
Begin
...
End;

Commentaire de jack169 le 03/09/2008 23:33:55

Bonjour,
Excusez-moi pour mon incompétence suprême en informatique, mais j'ai trouvé votre logiciel et il pourrait m'être bien utile. Malheureusement je n'ai aucune idée de comment l'ouvrir car le dossier est sous Win rar, mais une fois dedans je suis perdu.
Merci d'avance

Commentaire de geulbuif le 18/09/2008 13:02:23

JACK169 :

Il faut que tu decompresse tout les fichiers
(surtout fmd.ex_)

Puis renommer fmd.ex_ en fmd.exe
Ensuite tu lance le prog a partir de fmd.exe ;)
en espérant avoir été clair et t'avoir aider .

PS : si tu compte pas étudier le logiciel / programme tout les autres fichiers tu peut les supprimer ;)
En remerciant f0xi sa m'a bien aider :D

Commentaire de adelinezahnd le 03/12/2008 12:56:11

foxi !!!

ton logiciel est super !!!!!!!!

est-il libre ?
je désirerais l'utiliser en classe pour vérifier facilement les calculs de simplification de fractions et de nombre en général.

adeline

Commentaire de amiga68 le 03/12/2008 19:13:23

* Comme BruNews a pus me le demontrer, en C ces deux lignes pourrait etre beaucoup plus simple
        si Delphi possedait l'operateur ++ et -- ce qui serait le minimum quand même! (>_<) *
      }

DivisorsCount := DivisorsCount + 1;

Il existe inc(divisorsCount); mais je ne sais pas si c'est plus rapide...

 Ajouter un commentaire




Nos sponsors


Sondage...

CalendriCode

Mai 2013
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Photothèque

A découvrir



 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 1,108 sec (4)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales