Bitwise et à la place de l'opérateur de module

89

Nous savons que par exemple modulo de puissance de deux peut être exprimé comme ceci:

  x % 2 inpower n == x & (2 inpower n - 1).

Exemples:

x % 2 == x & 1
x % 4 == x & 3
x % 8 == x & 7 

Qu'en est-il de la non-puissance générale de deux nombres?

Disons:

x% 7 ==?

dato datuashvili
la source
8
@Neil - Modulo et Binary Et ce sont des opérations assez fondamentales, je suppose qu'elles sont à peu près les mêmes dans n'importe quel langage informatique.
James Kolpack
1
Je suis un peu fatigué de ne pas voir le langage affiché :) Bien que je suppose généralement que s'ils ne spécifient pas, je suppose que cela signifie C ++ ou C. Je me demande à quel point c'est vrai ...
Garet Claborn
1
Pour ceux qui ont du mal à comprendre cela, jetez un œil à stackoverflow.com/a/13784820/1414639 . Oh, et dans JS avec V8, j'obtiens une très légère amélioration des performances en utilisant des opérateurs au niveau du bit.
Bardi Harborow
1
@JamesKolpack Une opération au niveau du bit peut être effectuée BEAUCOUP plus rapidement sur un CPU qu'un modulo. En fait, une astuce d'assemblage courante pour mettre à zéro un registre est de le XOR avec lui-même (à cause de ce fait). De nos jours, un compilateur pourrait être capable d'optimiser un module d'une puissance de deux, mais je ne sais pas
Kaiser Keister

Réponses:

68

Tout d'abord, il n'est pas exact de dire que

x % 2 == x & 1

Simple contre - : x = -1. Dans de nombreuses langues, y compris Java, -1 % 2 == -1. Autrement dit, %n'est pas nécessairement la définition mathématique traditionnelle de modulo. Java l'appelle par exemple "opérateur de reste".

En ce qui concerne l'optimisation au niveau du bit, seules les puissances modulo de deux peuvent "facilement" être effectuées en arithmétique au niveau du bit. D'une manière générale, seules les puissances modulo de base b peuvent "facilement" être réalisées avec une représentation en base b des nombres.

En base 10, par exemple, pour non-négatif N, N mod 10^kprend juste les kchiffres les moins significatifs .

Références

lubrifiants polygènes
la source
1
-1 = -1 (mod 2), vous ne savez pas à quoi vous voulez en venir - vous voulez dire que ce n'est pas la même chose que le reste IEEE 754?
BlueRaja - Danny Pflughoeft
2
@BlueRaja: le résidu commun pour -1 dans le mod 2 est 1 en.wikipedia.org/wiki/Modular_arithmetic#Remainders
polygenelubricants
@BlueRaja: Si vous autorisez les nombres négatifs, ce dont vous pouvez être sûr (d'autant plus qu'aucun langage n'a été mentionné) est que (a / b) / b + a % b == a, pour les opérateurs de type C, a et b entiers, b non nul, et aussi cela abs(a % b) < abs(b)avec les mêmes conditions.
David Thornley
1
@DavidThornley - supposons que vous vouliez dire (a / b)* b + a % b == a.
sfjac
37

Il n'y a qu'un moyen simple de trouver un module de 2 ^ i nombres en utilisant le bit.

Il existe une manière ingénieuse de résoudre les cas de Mersenne selon le lien tel que n% 3, n% 7 ... Il existe des cas particuliers pour n% 5, n% 255 et des cas composites tels que n% 6.

Pour les cas 2 ^ i, (2, 4, 8, 16 ...)

n % 2^i = n & (2^i - 1)

Les plus compliqués sont difficiles à expliquer. Ne lisez que si vous êtes très curieux.

Sriram Murali
la source
1
vote ++; Excellent lien, merci pour la référence. Je conseille aux autres d'y jeter un œil, cela vaut la peine d'être lu même si c'est un peu compliqué.
varzeak
le lien est la meilleure partie de la réponse.
Amit Kumar
n% 2 ^ i = n & (1 << i - 1)
Kartik Singh
18

Cela ne fonctionne que pour les puissances de deux (et souvent uniquement positives) car elles ont la propriété unique de n'avoir qu'un seul bit mis à «1» dans leur représentation binaire. Étant donné qu'aucune autre classe de nombres ne partage cette propriété, vous ne pouvez pas créer d'expressions et de bits pour la plupart des expressions de module.

VeeArr
la source
1
Si vous travaillez sur une architecture ternaire, cela change un peu les choses ... les chances sont cependant nulles.
Noldorin
12

C'est spécifiquement un cas particulier car les ordinateurs représentent des nombres en base 2. Ceci est généralisable:

(nombre) base % base x

équivaut aux x derniers chiffres de la base (nombre) .

jdmichal
la source
5

Il existe des modules autres que les puissances de 2 pour lesquels des algorithmes efficaces existent.

Par exemple, si x est 32 bits non signé int alors x% 3 = popcnt (x & 0x55555555) - popcnt (x & 0xaaaaaaaa)

David Harris
la source
4

Modulo "7" sans opérateur "%"

int a = x % 7;

int a = (x + x / 7) & 7;
ashuwp
la source
3
Ne fonctionne pas pendant 10% 2 = 0. (10 + 10/2) & 2 = 15 & 2 = 2, de même 10% 6 = 4. (10 + 10/6) & 6 = 11 & 6 = 2
Sriram Murali
10
Aussi, pourquoi voudriez-vous diviser lorsque vous voulez éviter d'utiliser modulo? AFAIK, l'instruction de diviser est la même que celle pour obtenir le reste.
Horse SMith
1
@SriramMurali C'est parce que vous avez utilisé un mod pair, bien sûr que cela ne fonctionnerait pas, c'est une solution de contournement pour les impairs, comme l'a dit l'OP.
ylun.ca
3

Ne pas utiliser l' &opérateur binaire et ( ) en binaire, il n'y en a pas. Esquisse de preuve:

Supposons qu'il y ait une valeur k telle que x & k == x % (k + 1), mais k! = 2 ^ n - 1 . Alors si x == k , l'expression x & ksemble "fonctionner correctement" et le résultat est k . Maintenant, considérons x == ki : s'il y avait des bits "0" dans k , il y a un certain i supérieur à 0 qui ki ne peut être exprimé qu'avec 1 bits dans ces positions. (Par exemple, 1011 (11) doit devenir 0111 (7) lorsque 100 (4) en a été soustrait, dans ce cas, le bit 000 devient 100 lorsque i = 4. ) Si un bit de l'expression de k doit changer de zéro à un pour représenter ki, alors il ne peut pas calculer correctement x% (k + 1) , qui dans ce cas devrait être ki , mais il n'y a aucun moyen pour le booléen au niveau du bit et de produire cette valeur étant donné le masque.

Heath Hunnicutt
la source
2

Dans ce cas précis (mod 7), nous pouvons toujours remplacer% 7 par des opérateurs bit à bit:

// Return X%7 for X >= 0.
int mod7(int x)
{
  while (x > 7) x = (x&7) + (x>>3);
  return (x == 7)?0:x;
}

Cela fonctionne car 8% 7 = 1. Evidemment, ce code est probablement moins efficace qu'un simple x% 7, et certainement moins lisible.

Eric Bainville
la source
1

En utilisant bitwise_and, bitwise_or et bitwise_not, vous pouvez modifier n'importe quelle configuration de bit en une autre configuration de bit (c'est-à-dire que cet ensemble d'opérateurs est "fonctionnellement complet"). Cependant, pour des opérations comme le module, la formule générale serait forcément assez compliquée, je ne prendrais même pas la peine d'essayer de la recréer.

Mensonge Ryan
la source