La division dans le processeur prend beaucoup de temps, donc je veux demander comment vérifier de la manière la plus rapide si le nombre est divisible par un autre nombre. Dans mon cas, je dois vérifier si le nombre est divisible par 15.
En outre, j'ai parcouru le Web et trouvé fun des moyens de vérifier si un nombre est divisible par un certain nombre, mais je recherche une option rapide.
NOTE: comme la division prend beaucoup de temps, je cherche une réponse sans /
et %
.
La multiplication prend moins de temps que la division, vous pouvez donc essayer ceci:
inline bool divisible15(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
return x * 4008636143u <= 286331153u;
}
Cette méthode fonctionne, car 2^32-1
(la valeur maximale sur 32 bits) est divisible par 15. Cependant, si vous prenez, par exemple, 7, cela semblera fonctionner, mais ne fonctionnera pas dans tous les cas.
EDIT: Voir this , cela prouve que cette solution (sur certains compilateurs) est plus rapide que module.
EDIT:Ici est l'explication et la généralisation.
Réponse obligatoire pour les autres apprenants qui pourraient venir chercher une réponse.
if (number % n == 0)
Dans la plupart des cas, vous pouvez toujours le faire, en faisant confiance aux compilateurs modernes et intelligents.
Cela ne signifie pas pour autant que vous soyez découragé d'apprendre des méthodes amusantes. Découvrez ces liens.
Il suffit d'utiliser i % 15 == 0
Comme le compilateur peut facilement voir que 15 ne changera jamais, il peut faire toute optimisation qu'il souhaite sur l'opération mod. Il est du travail des auteurs de compilateur de faire ce genre d’optimisation s’ils n’ont pas pensé à une meilleure façon de le faire.
Par exemple, il est très facile de vérifier si un nombre est divisible par 2 car il suffit de vérifier le premier bit. Les rédacteurs de compilateur le savent et vous pouvez écrire le code vous-même pour vérifier le premier élément, mais un compilateur mature, en particulier, demandera aux gens de réfléchir et de travailler sur ces choses pendant des années. Ce type d’optimisation est très simple à réaliser car il ne nécessite que de changer une instruction ou 2, les optimisations comme une meilleure allocation de registre sont beaucoup plus difficiles à réaliser.
Une autre chose à considérer est que votre compilateur a été écrit pour le système sur lequel il est allumé, votre code est en revanche le même partout, si vous écrivez un code étrange qui peut être aussi rapide sur un système (probablement pas encore plus rapide) ) mais sur un autre système doté d’une optimisation matérielle spéciale, votre code peut perdre un ordre de grandeur. Puisque vous avez écrit du code ésotérique pour vérifier la divisibilité, il est peu probable que le compilateur réalise qu’il peut être optimisé pour une seule opération matérielle. Ecrire l’opération la plus évidente rend la vie meilleure et plus facile pour le compilateur.
Puisque vous n’avez pas réellement vérifié que la rapidité avec laquelle vous écrivez dans le code rendrait le code étrange, il serait très difficile à lire pour le prochain utilisateur et donc plus sujet aux erreurs ( l’optimisation prématurée est la racine de tout mal )
Cela fonctionne toujours que l'entrée soit 16, 32 ou 64 bits, car elle ne repose pas sur une manipulation de bits.
Même si l'auteur du compilateur ne l'a pas implémenté, il est clairement possible que quelqu'un l'implémente (même vous-même)
Sur un processus raisonnablement moderne, diviser par 15 ne devrait pas être si terrible. Le guide d'optimisation AMD le définit en fonction du quotient (la valeur en cours de division) et prend 8 positions de bit et plus du bit le plus significatif du quotient. Donc, si vos nombres ont le 63ème bit défini, vous vous retrouvez avec 71 cycles - ce qui est une instruction assez longue, bien sûr. Mais pour un nombre 32 bits avec quelques zéros dans les bits supérieurs, nous parlons de 30 à 40 cycles. Si le nombre correspond à une valeur de 16 bits, le maximum est de 23 cycles.
Pour obtenir le reste, il faut encore un autre cycle d'horloge.
Si vous faites cela TOUT le temps, bien sûr, vous constaterez que ce temps est assez long, mais je ne suis pas sûr qu'il existe un moyen trivial de l'éviter.
Comme d’autres l’ont dit, le compilateur pourrait peut-être le remplacer par quelque chose de meilleur. Mais à ma connaissance, 15 n’a pas une solution rapide évidente (si vous en avez 16 au lieu de 15, nous pouvons utiliser l’astuce de x & 15
).
Si la plage est limitée, vous pouvez créer une table [vector<bool>
par exemple, qui stockera 1 bit par entrée], mais vous rencontrerez bientôt le problème suivant: l'accès mémoire non mis en cache prend tout autant de temps qu'une opération de division. ...
Il existe des moyens intéressants de déterminer si un nombre est divisé par 3, 5, etc. en additionnant les chiffres, mais malheureusement, ceux-ci ne fonctionnent que sur la base de chiffres décimaux, ce qui implique une longue séquence de divisions.
Voici une autre approche, qui est probablement plus lente que d’autres, mais n’utilise que l’addition, bitwise-and et shift:
int divisible15(unsigned int x) {
if (x==0) return 1;
x = (x & 0x0f0f0f0f) + ((x&0xf0f0f0f0)>>4);
x = (x & 0x00ff00ff) + ((x&0xff00ff00)>>8);
x = (x & 0x0000ffff) + ((x&0xffff0000)>>16);
x = (x & 0x0f) + ((x&0xf0)>>4);
return x==15;
}
L'idée est que la divisibilité par 15, en base 16, est similaire à la divisibilité par 9 en base 10 - la somme des chiffres doit être divisible par 15.
Ainsi, le code additionne tous les chiffres hexadécimaux (de la même manière que vous comptez les bits) et la somme doit être égale à 15 (sauf pour 0).
Eh bien, c'est très facile à faire dans votre tête si vous avez la représentation hexadécimale. Faites simplement la somme de tous les chiffres, jusqu'à obtenir un seul chiffre. Si la réponse est '0xf', il est divisible par 15.
Exemple 0x3a98
: 3 + 0xa + 9 + 8 = 0x1e = 1 + 0xe = 0xf, donc divisible par 15.
Cela fonctionne pour tous les facteurs sur X-1, où X est la base utilisée pour représenter le nombre. (Pour les facteurs plus petits, le dernier chiffre doit être divisible par le facteur).
Ne vous attendez pas à ce que cela soit rapide dans le code, cependant.