galois.primitive_root¶
-
galois.primitive_root(n, start=
1
, stop=None
, reverse=False
)¶ Finds the smallest primitive root modulo
.- Parameters¶
- n : int¶
A positive integer.
- start : int¶
Starting value (inclusive) in the search for a primitive root. The default is 1. The resulting primitive root, if found, will be
.- stop : Optional[int]¶
Stopping value (exclusive) in the search for a primitive root. The default is
None
which corresponds to . The resulting primitive root, if found, will be .- reverse : bool¶
Search for a primitive root in reverse order, i.e. find the largest primitive root first. Default is
False
.
- Returns¶
The smallest primitive root modulo
. ReturnsNone
if no primitive roots exist.- Return type¶
Notes
The integer
is a primitive root modulo if the totatives of , the positive integers that are coprime with , can be generated by powers of .Alternatively said,
is a primitive root modulo if and only if is a generator of the multiplicative group of integers modulo ,where
is order of the group.If
is cyclic, the number of primitive roots modulo is given by .References
Shoup. Searching for primitive roots in finite fields. https://www.ams.org/journals/mcom/1992-58-197/S0025-5718-1992-1106981-9/S0025-5718-1992-1106981-9.pdf
Hua. On the least primitive root of a prime. https://www.ams.org/journals/bull/1942-48-10/S0002-9904-1942-07767-6/S0002-9904-1942-07767-6.pdf
http://www.numbertheory.org/courses/MP313/lectures/lecture7/page1.html
Examples
The elements of
are the positive integers less than that are coprime with . For example, .# n is of type 2*p^k, which is cyclic In [1]: n = 14 In [2]: galois.is_cyclic(n) Out[2]: True # The congruence class coprime with n In [3]: Znx = set([a for a in range(1, n) if math.gcd(n, a) == 1]); Znx Out[3]: {1, 3, 5, 9, 11, 13} # Euler's totient function counts the "totatives", positive integers coprime with n In [4]: phi = galois.euler_phi(n); phi Out[4]: 6 In [5]: len(Znx) == phi Out[5]: True # The primitive roots are the elements in Znx that multiplicatively generate the group In [6]: for a in Znx: ...: span = set([pow(a, i, n) for i in range(1, phi + 1)]) ...: primitive_root = span == Znx ...: print("Element: {:2d}, Span: {:<20}, Primitive root: {}".format(a, str(span), primitive_root)) ...: Element: 1, Span: {1} , Primitive root: False Element: 3, Span: {1, 3, 5, 9, 11, 13}, Primitive root: True Element: 5, Span: {1, 3, 5, 9, 11, 13}, Primitive root: True Element: 9, Span: {9, 11, 1} , Primitive root: False Element: 11, Span: {9, 11, 1} , Primitive root: False Element: 13, Span: {1, 13} , Primitive root: False # Find the smallest primitive root In [7]: galois.primitive_root(n) Out[7]: 3 # Find all primitive roots In [8]: roots = galois.primitive_roots(n); roots Out[8]: [3, 5] # Euler's totient function ϕ(ϕ(n)) counts the primitive roots of n In [9]: len(roots) == galois.euler_phi(phi) Out[9]: True
A counterexample is
, which doesn’t fit the condition for cyclicness. .# n is of type p1^k1 * p2^k2, which is not cyclic In [10]: n = 15 In [11]: galois.is_cyclic(n) Out[11]: False # The congruence class coprime with n In [12]: Znx = set([a for a in range(1, n) if math.gcd(n, a) == 1]); Znx Out[12]: {1, 2, 4, 7, 8, 11, 13, 14} # Euler's totient function counts the "totatives", positive integers coprime with n In [13]: phi = galois.euler_phi(n); phi Out[13]: 8 In [14]: len(Znx) == phi Out[14]: True # The primitive roots are the elements in Znx that multiplicatively generate the group In [15]: for a in Znx: ....: span = set([pow(a, i, n) for i in range(1, phi + 1)]) ....: primitive_root = span == Znx ....: print("Element: {:2d}, Span: {:<13}, Primitive root: {}".format(a, str(span), primitive_root)) ....: Element: 1, Span: {1} , Primitive root: False Element: 2, Span: {8, 1, 2, 4} , Primitive root: False Element: 4, Span: {1, 4} , Primitive root: False Element: 7, Span: {1, 4, 13, 7}, Primitive root: False Element: 8, Span: {8, 1, 2, 4} , Primitive root: False Element: 11, Span: {1, 11} , Primitive root: False Element: 13, Span: {1, 4, 13, 7}, Primitive root: False Element: 14, Span: {1, 14} , Primitive root: False # Find the smallest primitive root In [16]: galois.primitive_root(n) # Find all primitive roots In [17]: roots = galois.primitive_roots(n); roots Out[17]: [] # Note the max order of any element is 4, not 8, which is Carmichael's lambda function In [18]: galois.carmichael_lambda(n) Out[18]: 4
The algorithm is also efficient for very large
.In [19]: n = 1000000000000000035000061 In [20]: galois.primitive_root(n) Out[20]: 7