Trial division can be done by successive division of an integer n by all numbers less than the sqrt of n. As we technically only need to check if n is divisible by primes, we can reduce operations by performing divisions on a smaller set with greater prime density. Examples of such sets are the odd numbers Z+1, which can be further extended to the p-rough numbers.
Let SP be the set of primes less than or equal to an arbitrary prime p. Then let PR be the set of p-rough numbers. As PR set includes all primes greater than or equal to p we can speed up our primality check by only checking the divisibility of n by all the elements of PR less than the sqrt of n as long as we check if it is not any of the primes smaller than p
trial_division(n)
if n ∈ SP
return true
end if
if ∃ x ∈ PR := x|n
return false
end if
return true
A proper implementation of this requires producing a generating function for the p-rough numbers. Suppose that we have a n such that n is the product of all primes less than p then we have the following generating function n * k ± x ∀ x ∈ S where S is the set of all primes in the interval [p;n/2]
We show an example using the 7-rough numbers
trialdivision(n)
primorial <- 30 // 2*3*5
rough_gen <- [1,7,11,13] // P - {2,3,5} + 1
primes <- [2,3,5,7,11,13]
if n == 1
return false
end if
for i in primes // checks if n ∈ primes
if n == i
return true
end if
if i|n // checks if n is divisible by i ∈ primes
return false
end if
end for
suprenum <- (sqrt(n)/primorial) + 1
for i in [1;suprenum]
for j in rough_gen
if (primorial*i - j)|x ∨ (primorial*i+j)|x
return false
end if
end for
end for
return true
The pseudoprime test. This checks if the condition ap-1 = 1 mod p holds true. If so then a is called a weak witness to the primality of p. For instance we want to check the primality of 341, so we use 2 as the base and checking that 2340 mod 341 = 1. So 2 is a witness for the primality of 341. However if we factor 341 we see that it is infact composite (11*31) making 2 a Fermat liar.
sprp(p,base)
zeroes <- trailing_zeros(p-1) // Real implementation can be found in builtin_ctz (G++ C++) or trailing_zeros (Rust) or TZCNT (Intel instruction)
d <- (p-1)/ 2^zeroes
x <- base^d mod p
if x == 1u64 ∨ x==p-1
return true
end if
loop zeroes-1 times
x <- x^2 mod p
if x == p-1
return true
end if
end loop
return false
Given the SPRP we have before we can make a random test that shows primality with high probability.We do this by setting the base using a randomly-generated number. One thing to note is that you need a rand number in a specific range. It must be between 2 and p-2, we can use the simple function 2 + rand mod (p - 4) to ensure that.
To produce a deterministic check in a interval of numbers [1;2^64-1] we only need to check for a small range of bases
det_miller(p)
base <- [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
for i in base
if p == i
return true
end if
if sprp(p,i) == false
return false
end if
return true