Name |
Properties (for all a,
b, and c) |
Examples |
Magma | Closure: a • b exists | Quaternion multiplication. |
Semigroup | (all of the above, plus) Associativity: (a • b) • c = a • (b • c) |
The positive integers under addition ( • is +). |
Monoid | (all of the above, plus) Identity: 0 exists, where a • 0 = a |
Non-negative integers under addition. |
Group* |
(all of the above, plus) Inverse: -a exists, where a • -a = 0 |
All integers under addition. The integers under modular addition. Reals under multiplication. Integers under addition, modulo a prime. Multiplication of invertible matrices. |
Abelian Group |
(all of the above, plus) Commutativity: a • b = b • a |
All of the above, except matrix
multiplication. |
Ring |
With "addition", forms an abelian group. Also has an associative "multiplication" operation. Distributivity: a*(b+c) = a*b+a*c |
The integers under addition and
multiplication. The reals under addition and multiplication. |
Field* |
(all properties of a commutative ring, plus) Nonzero elements can be "divided": multiplicative inverses exist. |
There are quite a few infinite fields (reals,
complex, rationals). Yet every finite field of the same size is equivalent (isomorphic), a surprising result. |
0 0 0 0 0 1 2 3 0 2 0 2 0 3 2 1The problem is that multiplying by 2 doesn't have an inverse--not only does 2*2 result in the excluded zero element, 2*1=2*3=2, which means we don't have multiplicative inverses, so Z%4 is not a field.
0 0 0 0 0 0 1 2 3 4 0 2 4 1 3 0 3 1 4 2 0 4 3 2 1(Try this in NetRun now!)
Since Z%n is a group under
multiplication, and groups have inverses, it's possible to find
the multiplicative inverse of a number, where the product of a
number and its inverse gives 1. For example, in Z%5, the
multiplicative inverse of 3 is 2, since 3*2=1 mod 5. This is
also called the modular inverse, since 3*2=6 without the modulus.
It is possible
to find the modular inverse via brute force, by checking
every number between 1 and p, but this takes 2N
checks for an N-bit number. It is much more efficient to use
the extended
euclidean greatest common denominator algorithm (EGCD) to
find modular inverses. The EGCD algorithm finds the
constants x and y such that:
gcd(a,b) = ax + by
We run the
EGCD algorithm on the multiplier a and the prime P. Because
P is prime, the gcd of anything smaller and P is 1, so we'll get
constants x and y such that:
1 = ax + Py
Since we're
operating modulo the prime, we don't care about y, and we have:
1 = ax mod P
This indicates x is the modular inverse of a.
The EGCD
algorithm operates by maintaining the invariant that ax + by is
our estimate of the gcd. Initially, our estimate is
that the gcd is either a or b, so x and y are 0 and 1. As we
reduce
the estimate by repeatedly subtracting off copies of a and
b, we adjust x and y to compensate.
For example,
if we want to calculate the modular inverse of 10 mod 13, the EGCD
would calculate:
gcd = a * x + b * y
13 = 10 * 0 + 13 * 1 10 = 10 * 1 + 13 * 0 3 = 10 * -1 + 13 * 1 1 = 10 * 4 + 13 * -3
Here's
Z%13. 10's modular inverse is indeed 4 (4*10=1 mod
13).
mod13 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 2 3 4 5 6 7 8 9 10 11 12 0 2 0 2 4 6 8 10 12 1 3 5 7 9 11 0 3 0 3 6 9 12 2 5 8 11 1 4 7 10 0 4 0 4 8 12 3 7 11 2 6 10 1 5 9 0 5 0 5 10 2 7 12 4 9 1 6 11 3 8 0 6 0 6 12 5 11 4 10 3 9 2 8 1 7 0 7 0 7 1 8 2 9 3 10 4 11 5 12 6 0 8 0 8 3 11 6 1 9 4 12 7 2 10 5 0 9 0 9 5 1 10 6 2 11 7 3 12 8 4 0 10 0 10 7 4 1 11 8 5 2 12 9 6 3 0 11 0 11 9 7 5 3 1 12 10 8 6 4 2 0 12 0 12 11 10 9 8 7 6 5 4 3 2 1 0 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Here's a
Python translation of the EGCD algorithm. The commas make
tuples, used to perform multiple assignments in one step.
from time import time; # From http://rosettacode.org/wiki/Modular_inverse#Python def extended_gcd(aa, bb): lastrem, rem = abs(aa), abs(bb) x, lastx, y, lasty = 0, 1, 1, 0 while rem: lastrem, (quotient, rem) = rem, divmod(lastrem, rem) x, lastx = lastx - quotient*x, x y, lasty = lasty - quotient*y, y return lastrem, lastx * (-1 if aa < 0 else 1), lasty * (-1 if bb < 0 else 1) def modinv(a, m): g, x, y = extended_gcd(a, m) if g != 1: raise ValueError return x % m # This is the prime modulus used by SECG secp256k1, the BitCoin elliptic curve P=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; G=2; E=0xabcdef1234559daaaaaaaaaaaaaadeadbeefaaaaaaaaaaaaaaaaaaaaffff0008; for repeat in range(3): start=time(); Ei=modinv(E,P); print("Product = ",(E*Ei)%P); elapsed=time()-start; print("Elapsed=",elapsed," seconds");
This runs in
0.1 milliseconds, and produces a product of 1.
Comparing
Python to my hastily coded C++ big number library:
#include "osl/bignum.h" typedef bignum<256> integer; // This is the prime modulus used by SECG secp256k1, the BitCoin elliptic curve integer P("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"); integer G=2; integer E("0xabcdef1234559daaaaaaaaaaaaaadeadbeefaaaaaaaaaaaaaaaaaaaaffff0008"); long foo(void) { integer Ei=E.modInverse(P); return (Ei*E).mod(P)==1; }This runs in 3.4 milliseconds, 34 times slower than Python! I suspect the problem is my divide implementation doesn't use 32-bit machine integers, but operates one bit at a time. It's still embarrassingly slow!
from time import time; # This is the prime modulus used by SECG secp256k1, the BitCoin elliptic curve P=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; G=2; E=0xabcdef1234559daaaaaaaaaaaaaadeadbeefaaaaaaaaaaaaaaaaaaaaffff0008; for repeat in range(3): start=time(); res=pow(G,E,P); elapsed=time()-start; print("Elapsed=",elapsed," seconds");
(Try
this in NetRun now!)
This runs in 0.14 milliseconds. My library runs in 28
milliseconds.