mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-27 23:11:09 +00:00
bc8df6de31
* [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.2.2 → v0.3.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.2.2...v0.3.2) - [github.com/pre-commit/mirrors-mypy: v1.8.0 → v1.9.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.8.0...v1.9.0) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
107 lines
2.7 KiB
Python
107 lines
2.7 KiB
Python
"""
|
|
This script implements the Solovay-Strassen Primality test.
|
|
|
|
This probabilistic primality test is based on Euler's criterion. It is similar
|
|
to the Fermat test but uses quadratic residues. It can quickly identify
|
|
composite numbers but may occasionally classify composite numbers as prime.
|
|
|
|
More details and concepts about this can be found on:
|
|
https://en.wikipedia.org/wiki/Solovay%E2%80%93Strassen_primality_test
|
|
"""
|
|
|
|
import random
|
|
|
|
|
|
def jacobi_symbol(random_a: int, number: int) -> int:
|
|
"""
|
|
Calculate the Jacobi symbol. The Jacobi symbol is a generalization
|
|
of the Legendre symbol, which can be used to simplify computations involving
|
|
quadratic residues. The Jacobi symbol is used in primality tests, like the
|
|
Solovay-Strassen test, because it helps determine if an integer is a
|
|
quadratic residue modulo a given modulus, providing valuable information
|
|
about the number's potential primality or compositeness.
|
|
|
|
Parameters:
|
|
random_a: A randomly chosen integer from 2 to n-2 (inclusive)
|
|
number: The number that is tested for primality
|
|
|
|
Returns:
|
|
jacobi_symbol: The Jacobi symbol is a mathematical function
|
|
used to determine whether an integer is a quadratic residue modulo
|
|
another integer (usually prime) or not.
|
|
|
|
>>> jacobi_symbol(2, 13)
|
|
-1
|
|
>>> jacobi_symbol(5, 19)
|
|
1
|
|
>>> jacobi_symbol(7, 14)
|
|
0
|
|
"""
|
|
|
|
if random_a in (0, 1):
|
|
return random_a
|
|
|
|
random_a %= number
|
|
t = 1
|
|
|
|
while random_a != 0:
|
|
while random_a % 2 == 0:
|
|
random_a //= 2
|
|
r = number % 8
|
|
if r in (3, 5):
|
|
t = -t
|
|
|
|
random_a, number = number, random_a
|
|
|
|
if random_a % 4 == number % 4 == 3:
|
|
t = -t
|
|
|
|
random_a %= number
|
|
|
|
return t if number == 1 else 0
|
|
|
|
|
|
def solovay_strassen(number: int, iterations: int) -> bool:
|
|
"""
|
|
Check whether the input number is prime or not using
|
|
the Solovay-Strassen Primality test
|
|
|
|
Parameters:
|
|
number: The number that is tested for primality
|
|
iterations: The number of times that the test is run
|
|
which effects the accuracy
|
|
|
|
Returns:
|
|
result: True if number is probably prime and false
|
|
if not
|
|
|
|
>>> random.seed(10)
|
|
>>> solovay_strassen(13, 5)
|
|
True
|
|
>>> solovay_strassen(9, 10)
|
|
False
|
|
>>> solovay_strassen(17, 15)
|
|
True
|
|
"""
|
|
|
|
if number <= 1:
|
|
return False
|
|
if number <= 3:
|
|
return True
|
|
|
|
for _ in range(iterations):
|
|
a = random.randint(2, number - 2)
|
|
x = jacobi_symbol(a, number)
|
|
y = pow(a, (number - 1) // 2, number)
|
|
|
|
if x == 0 or y != x % number:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import doctest
|
|
|
|
doctest.testmod()
|