Je sais que l'algorithme d'Euclide est le meilleur algorithme pour obtenir le GCD (grand diviseur commun) d'une liste d'entiers positifs. Mais en pratique, vous pouvez coder cet algorithme de différentes manières. (Dans mon cas, j'ai décidé d'utiliser Java, mais C / C ++ peut être une autre option).
J'ai besoin d'utiliser le code le plus efficace possible dans mon programme.
En mode récursif, vous pouvez écrire:
static long gcd (long a, long b){
a = Math.abs(a); b = Math.abs(b);
return (b==0) ? a : gcd(b, a%b);
}
Et en mode itératif, cela ressemble à ceci:
static long gcd (long a, long b) {
long r, i;
while(b!=0){
r = a % b;
a = b;
b = r;
}
return a;
}
Il y a aussi l'algorithme binaire pour le GCD, qui peut être codé simplement comme ceci:
int gcd (int a, int b)
{
while(b) b ^= a ^= b ^= a %= b;
return a;
}
algorithms
recursion
arithmetic
jonaprieto
la source
la source
Réponses:
Vos deux algorithmes sont équivalents (au moins pour les entiers positifs, ce qui se passe avec les entiers négatifs dans la version impérative dépend de la sémantique de Javauneje bje je
%
que je ne connais pas par cœur). Dans la version récursive, soit et b i l'argument du i ème appel récursif: a i + 1 = b i b i + 1 = a i m o d b iDans la version impérative, soit et b ′ i les valeurs des variables et au début de la i ème itération de la boucle. a ′ i + 1 = b ′ i b ′ i + 1 = a ′ i m o d b ′ iune′je b′je je
a
b
Vous remarquez une ressemblance? Votre version impérative et votre version récursive calculent exactement les mêmes valeurs. En outre, ils ont tous deux se terminent au même moment, quand (resp. Un ' i = 0 ), de sorte qu'ils effectuent le même nombre d'itérations. Donc, algorithmiquement parlant, il n'y a pas de différence entre les deux. Toute différence sera une question d'implémentation, très dépendante du compilateur, du matériel sur lequel il s'exécute, et très probablement du système d'exploitation et des autres programmes exécutés simultanément.uneje= 0 une′je= 0
La version récursive ne fait que des appels récursifs de queue . La plupart des compilateurs pour les langages impératifs ne les optimisent pas, et il est donc probable que le code qu'ils génèrent perdra un peu de temps et de mémoire à construire un cadre de pile à chaque itération. Avec un compilateur qui optimise les appels de queue (les compilateurs pour les langages fonctionnels le font presque toujours), le code machine généré peut bien être le même pour les deux (en supposant que vous harmonisez ces appels à
abs
).la source
Pour les nombres petits, l'algorithme GCD binaire est suffisant.
GMP, une bibliothèque bien entretenue et testée dans le monde réel, passera à un algorithme spécial demi-GCD après avoir passé un seuil spécial, une généralisation de l'algorithme de Lehmer. Lehmer utilise la multiplication matricielle pour améliorer les algorithmes euclidiens standard. Selon les documents, le temps de fonctionnement asymptotique du HGCD et du GCD est
O(M(N)*log(N))
, oùM(N)
est le temps pour multiplier deux nombres de N-membres.Tous les détails sur leur algorithme peuvent être trouvés ici .
la source
Comme je sais, Java ne prend pas en charge l'optimisation de la récursivité de queue en général, mais vous pouvez tester votre implémentation Java pour cela; s'il ne le supporte pas, une simple
for
boucle devrait être plus rapide, sinon la récursivité devrait être tout aussi rapide. D'un autre côté, ce sont des optimisations de bits, choisissez le code que vous pensez être plus facile et plus lisible.Je dois également noter que l'algorithme GCD le plus rapide n'est pas l'algorithme d'Euclide, l'algorithme de Lehmer est un peu plus rapide.
la source
Tout d'abord, n'utilisez pas la récursivité pour remplacer une boucle serrée. C'est lent. Ne comptez pas sur le compilateur pour l'optimiser. Deuxièmement, dans votre code, vous appelez Math.abs () dans chaque appel récursif, ce qui est inutile.
Dans votre boucle, vous pouvez facilement éviter les variables temporaires et permuter tout le temps a et b.
L'échange à l'aide de a ^ = b ^ = a ^ = b raccourcit la source mais nécessite de nombreuses instructions à exécuter. Il sera plus lent que l'échange ennuyeux avec une variable temporaire.
la source
Pour les petits nombres ,% est une opération assez coûteuse, peut-être la récursive la plus simple
est plus rapide? (Désolé, code Mathematica et non C ++)
la source
L'algorithme Euclid est le plus efficace pour calculer GCD:
Exemple:-
la source