Bitowo i zamiast operatora modułu

91

Wiemy, że na przykład modulo potęgi dwóch można wyrazić w ten sposób:

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

Przykłady:

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

A co z ogólną niepotęgą dwóch liczb?

Powiedzmy:

x% 7 ==?

dato datuashvili
źródło
8
@Neil - Modulo i Binary Są to dość podstawowe operacje, myślę, że są mniej więcej takie same w każdym języku komputerowym.
James Kolpack
1
Jestem trochę zmęczony tym, że nie widzę opublikowanego języka :) Chociaż myślę, że zwykle, jeśli nie podają, zakładam, że oznacza to C ++ lub C. Zastanawiam się, czy to prawda ..
Garet Claborn
1
Tylko dla każdego, kto ma problem ze zrozumieniem tego, zajrzyj na stackoverflow.com/a/13784820/1414639 . Aha, iw JS z V8 uzyskuję bardzo niewielki wzrost wydajności, używając operatorów bitowych.
Bardi Harborow
1
@JamesKolpack Operacja bitowa może być wykonana DUŻO szybciej na CPU niż na modulo. W rzeczywistości typową sztuczką asemblacyjną do wyzerowania rejestru jest XOR (z powodu tego faktu). W dzisiejszych czasach kompilator może być w stanie zoptymalizować modulo potęgi dwóch, ale nie wiem
Kaiser Keister

Odpowiedzi:

70

Po pierwsze, tak naprawdę nie jest to prawdą

x % 2 == x & 1

Proste kontrprzykład: x = -1. W wielu językach, w tym Java -1 % 2 == -1. Oznacza to, że %niekoniecznie jest to tradycyjna matematyczna definicja modulo. Na przykład Java nazywa to „operatorem reszty”.

Jeśli chodzi o optymalizację bitową, tylko potęgi modulo równe dwóm można „łatwo” wykonać w arytmetyce bitowej. Ogólnie mówiąc, tylko potęgi modulo o podstawie b można „łatwo” wykonać z reprezentacją liczb o podstawie b .

W bazie 10, na przykład, dla nieujemnych N, N mod 10^kjest po prostu biorąc najmniej znaczących kcyfr.

Bibliografia

smary wielogenowe
źródło
1
-1 = -1 (mod 2), nie jesteś pewien, do czego zmierzasz - masz na myśli to, że to nie to samo, co reszta IEEE 754?
BlueRaja - Danny Pflughoeft
2
@BlueRaja: częstą pozostałością dla -1 w modzie 2 jest 1 en.wikipedia.org/wiki/Modular_arithmetic#Remainders
polygenelubricants
@BlueRaja: Jeśli pozwolisz na liczby ujemne, w zasadzie możesz być pewien (zwłaszcza, że ​​nie wspomniano o żadnym języku), że (a / b) / b + a % b == adla operatorów typu C, liczby całkowite a i b, b niezerowe, a także te abs(a % b) < abs(b)z tymi samymi zastrzeżeniami.
David Thornley
1
@DavidThornley - załóżmy, że masz na myśli (a / b)* b + a % b == a.
sfjac
40

Jest tylko prosty sposób na znalezienie modulo 2 ^ i liczb za pomocą bitów.

Istnieje genialny sposób rozwiązywania przypadków Mersenne'a zgodnie z linkiem, takim jak n% 3, n% 7 ... Istnieją specjalne przypadki dla n% 5, n% 255 i przypadki złożone, takie jak n% 6.

W przypadkach 2 ^ i, (2, 4, 8, 16 ...)

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

Bardziej skomplikowane są trudne do wyjaśnienia. Czytaj tylko, jeśli jesteś bardzo ciekawy.

Sriram Murali
źródło
1
głosowanie ++; Doskonały link, dzięki za odniesienie. Radzę innym rzucić okiem, warto ją przeczytać, nawet jeśli jest trochę skomplikowana.
varzeak
link jest najlepszą częścią odpowiedzi.
Amit Kumar,
n% 2 ^ i = n & (1 << i - 1)
Kartik Singh
18

Działa to tylko dla potęg dwójki (i często tylko dodatnich), ponieważ mają one unikalną właściwość posiadania tylko jednego bitu ustawionego na „1” w ich binarnej reprezentacji. Ponieważ żadna inna klasa liczb nie ma tej właściwości, nie można tworzyć wyrażeń bitowych i dla większości wyrażeń modułu.

VeeArr
źródło
2
Jeśli zdarzy się, że pracujesz w architekturze trójskładnikowej, to zmienia to trochę ... szanse są jednak bliskie zeru.
Noldorin
Podoba mi się, jak to wyrażasz: „ to trochę zmienia ”
j3141592653589793238
12

Jest to szczególnie szczególny przypadek, ponieważ komputery reprezentują liczby o podstawie 2. Można to uogólnić:

(liczba) podstawa % podstawa x

jest równy ostatnim x cyfrom podstawy (liczby) .

jdmichal
źródło
5

Istnieją moduły inne niż potęgi 2, dla których istnieją wydajne algorytmy.

Na przykład, jeśli x to 32 bity bez znaku int, a następnie x% 3 = popcnt (x & 0x55555555) - popcnt (x & 0xaaaaaaaa)

David Harris
źródło
4

Modulo „7” bez operatora „%”

int a = x % 7;

int a = (x + x / 7) & 7;
ashuwp
źródło
3
Nie działa dla 10% 2 = 0 (10 + 10/2) i 2 = 15 i 2 = 2, podobnie 10% 6 = 4. (10 + 10/6) i 6 = 11 i 6 = 2
Sriram Murali
10
Dlaczego miałbyś chcieć dzielić, skoro nie chcesz używać modulo? AFAIK, instrukcja dzielenia jest taka sama, jak ta, aby uzyskać resztę.
Horse SMith
2
@SriramMurali To dlatego, że użyłeś parzystego mod, oczywiście to nie zadziała, jest to obejście dla dziwnych, jak powiedział OP.
ylun.ca
3

Nieużywanie bitowych i (& operatora ) w systemie binarnym, nie ma. Szkic dowodu:

Załóżmy, że było wartością K w taki sposób, x & k == x % (k + 1)ale K = 2 ^ n - 1 . Wtedy, jeśli x == k , wyrażenie x & kwydaje się „działać poprawnie”, a wynikiem jest k . Rozważmy teraz x == ki : jeśli w k było jakieś „0” bitów , istnieje i większe od 0, które ki może być wyrażone tylko za pomocą 1-bitów w tych pozycjach. (Np. 1011 (11) musi stać się 0111 (7) po odjęciu od niego 100 (4), w tym przypadku bit 000 staje się 100, gdy i = 4 ). Jeśli bit z wyrażenia k musi zmienić się od zera do jednego, który reprezentuje ki, to nie może poprawnie obliczyć x% (k + 1) , co w tym przypadku powinno być ki , ale nie ma sposobu na bitowe wartości logiczne i uzyskanie tej wartości podanej masce.

Heath Hunnicutt
źródło
2

W tym konkretnym przypadku (mod 7) nadal możemy zastąpić% 7 operatorami bitowymi:

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

Działa, ponieważ 8% 7 = 1. Oczywiście ten kod jest prawdopodobnie mniej wydajny niż zwykły x% 7 iz pewnością mniej czytelny.

Eric Bainville
źródło
1

Używając bitwise_and, bitwise_or i bitwise_not można modyfikować dowolne konfiguracje bitów na inne konfiguracje bitowe (tj. Te zestawy operatorów są „funkcjonalnie kompletne”). Jednak w przypadku operacji takich jak moduł ogólny wzór byłby z konieczności dość skomplikowany, nawet nie zawracałbym sobie głowy próbą odtworzenia go.

Lie Ryan
źródło