Saturday, February 13, 2010

[nvvsbkrb] Primes with primitive root 2

The motivation for this exercise is a screen transition of the "pedal" screensaver, which plots a series of black pixels "randomly" until the screen is blank (actually, it gives up before that). Suppose we wish to have a "random" (this is a graphics application; it need only look random to the eye) permutation of all the pixels on the screen.

We generate pixel coordinates (linearized 1..(m*n)) with the recurrence c <- 2*c mod p, where p is chosen to be slightly larger than m*n, then skipping c if it is larger than m*n. We require primes with primitive root 2. We use 2 because, up to 2^31-1, we can do modular multiplication without overflow in 32 bits.

Essentially, we run through the whole period of a (probably bad) linear congruential random number generator.

Here is the next suitable prime (the delta to the next prime) after certain common screen resolutions.

*Main> next_primitive_2(640*480)
43
*Main> next_primitive_2(800*600)
19
*Main> next_primitive_2(1024*768)
37
*Main> next_primitive_2(1280*1024)
21
*Main> next_primitive_2(1600*1200)
11
*Main> next_primitive_2(1280*768)
29
*Main> next_primitive_2(1366*768)
5
*Main> next_primitive_2(1280*800)
21
*Main> next_primitive_2(1440*900)
11
*Main> next_primitive_2(1680*1050)
53
*Main> next_primitive_2(1920*1200)
11
*Main> next_primitive_2(1920*1080)
13
*Main> next_primitive_2(2560*1600)
13

For example, VGA (640 x 480) generates the sequence [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 217045, 126847, 253694, 200145, 93047, 186094] where the modulus is 640*480+43 = 307243 .

demonstration of screen blanking pixel by pixel

Animation of 128x96 (modulus is 128*96+13), magnified by a factor of 2. code

Exercising Pari/GP to see how many there are.

? default(timer,1)
? default(primelimit,2^31+10)
time = 5,120 ms.
%2 = 2147483658
? for(i=1,31,z=0;forprime(p=2^(i-1)+1,2^i,z=z+isprimroot(2,p));print(i," ",z))

1 1
2 1
3 1
4 2
5 2
6 4
7 4
8 10
9 15
10 29
11 51
12 101
13 175
14 316
15 601
16 1131
17 2148
18 3993
19 7635
20 14427
21 27566
22 52564
23 100322
24 192059
25 368570
26 708620
27 1363431
28 2628113
29 5071530
30 9800737
31 18957386               
time = 47mn, 47,672 ms.

Some Haskell code, including factoring by trial division. Note that "a" needs to be at least Int64 in order to handle up to 2^32. Assuming we are interested only in small integers, how much faster can this routine become? So many opportunities for clever algorithmic and number theoretic tricks.

Hardcoding 39301545 (or 39301544 not including 2) integers is not a great option. (Sum of the numbers above.)

next_primitive_2 :: forall a . (Integral (a)) => a -> a; next_primitive_2 x = (let { divide_test_seq :: a -> [](a) -> Maybe([](a)); divide_test_seq n xs = (case xs of { ((:)(x) (xrest))-> (case ((<) n ((*) x x)) of { (True)-> Nothing; (_)-> (case (mod n x) of { (0)-> (Just xs); (_)-> (divide_test_seq n xrest) }) }) }); factor :: a -> [](a); factor n = (factor_with_seq n prime_seq); factor_with_seq :: a -> [](a) -> [](a); factor_with_seq n nums = (case (compare n 1) of { (EQ)-> []; (GT)-> (case (divide_test_seq n nums) of { (Nothing)-> [n]; (Just(xs))-> ((:) (head xs) (factor_with_seq (div n (head xs)) xs)) }) }); is_prime :: a -> Bool; is_prime n = ((&&) ((<) 1 n) (isNothing(trial_division(n)))); is_primitive :: a -> a -> Bool; is_primitive x m = (and((map ((/=) 1))((test_prim_list m x)))); mod_exp :: a -> a -> a -> a; mod_exp m x n = (case (compare n 1) of { (EQ)-> x; (GT)-> (case (divMod n 2) of { ((q), (0))-> (mod_square m (mod_exp m x q)); ((q), (1))-> (mod_mult m x (mod_square m (mod_exp m x q))) }) }); mod_mult :: a -> a -> a -> a; mod_mult modulus x y = (mod ((*) x y) modulus); mod_square :: a -> a -> a; mod_square m x = (mod_mult m x x); prime_seq :: [](a); prime_seq = ((:) 2 (enumFromThen 3 5)); test_prim_list :: a -> a -> [](a); test_prim_list m x = (map (mod_exp m x) (unique_factors_div (pred m))); trial_division :: a -> Maybe([](a)); trial_division n = (divide_test_seq n prime_seq); unique_factors_div :: a -> [](a); unique_factors_div n = (map (div n) (map head (group (factor n)))) } in ((-) (head((filter (is_primitive 2))((filter is_prime)((enumFrom x))))) x))

No comments :