Smart algorithm for finding perfect numbers
Question:
Is there an algorithm that is quicker than O(N^2) for finding perfect numbers from a sample 1:N
?
Or any general speed improvements to do less computation?
I know we can remove odd numbers from the sample if we assume they are not perfect (unproven but we can assume it here regardless).
Answers:
Here is a way to do it (num
is your number):
if sum(i for i in range (1, num) if num % i == 0) == num:
print(num, "is a perfect number")
else:
print(num, "is not a perfect number")
EDIT (credits: @cdlane)
There is a one-to-one correspondence between the Mersenne primes and the even perfect numbers. You can use this fact to generate Mersenne primes with the Lucas–Lehmer primality test and get eventually a perfect number:
def lucas_lehmer(p):
s = 4
m = 2 ** p - 1
for _ in range(p - 2):
s = ((s * s) - 2) % m
return s == 0
def is_prime(number):
if number % 2 == 0:
return number == 2
i = 3
while i * i <= number:
if number % i == 0:
return False
i += 2
return True
for num in range(3, N, 2):
if is_prime(num) and lucas_lehmer(num):
print(2 ** (num - 1) * (2 ** num - 1), "is a perfect number")
Output (with N=500):
28 is a perfect number
496 is a perfect number
8128 is a perfect number
33550336 is a perfect number
8589869056 is a perfect number
137438691328 is a perfect number
2305843008139952128 is a perfect number
2658455991569831744654692615953842176 is a perfect number
191561942608236107294793378084303638130997321548169216 is a perfect number
13164036458569648337239753460458722910223472318386943117783728128 is a perfect number
14474011154664524427946373126085988481573677491474835889066354349131199152128 is a perfect number
You can get a significant improvement by not searching for perfect numbers but rather search for Mersenne primes, and then output their companion perfect number:
def lucas_lehmer_test(p):
s = 4
m = 2 ** p - 1
for _ in range(p - 2):
s = ((s * s) - 2) % m
return s == 0
def is_odd_prime(number):
"""
Efficiency of this doesn't matter as we're only using it to
test primeness of exponents not the mersenne primes themselves
"""
i = 3
while i * i <= number:
if number % i == 0:
return False
i += 2
return True
print(6) # to simplify code, treat first perfect number as a special case
for n in range(3, 5000, 2): # generate up to M20, found in 1961
if is_odd_prime(n) and lucas_lehmer_test(n):
print(2**(n - 1) * (2**n - 1))
Not perfect, but far better than trying to factor numbers looking for perfect ones.
Is there an algorithm that is quicker than O(N^2) for finding perfect numbers from a sample 1:N
?
Or any general speed improvements to do less computation?
I know we can remove odd numbers from the sample if we assume they are not perfect (unproven but we can assume it here regardless).
Here is a way to do it (num
is your number):
if sum(i for i in range (1, num) if num % i == 0) == num:
print(num, "is a perfect number")
else:
print(num, "is not a perfect number")
EDIT (credits: @cdlane)
There is a one-to-one correspondence between the Mersenne primes and the even perfect numbers. You can use this fact to generate Mersenne primes with the Lucas–Lehmer primality test and get eventually a perfect number:
def lucas_lehmer(p):
s = 4
m = 2 ** p - 1
for _ in range(p - 2):
s = ((s * s) - 2) % m
return s == 0
def is_prime(number):
if number % 2 == 0:
return number == 2
i = 3
while i * i <= number:
if number % i == 0:
return False
i += 2
return True
for num in range(3, N, 2):
if is_prime(num) and lucas_lehmer(num):
print(2 ** (num - 1) * (2 ** num - 1), "is a perfect number")
Output (with N=500):
28 is a perfect number
496 is a perfect number
8128 is a perfect number
33550336 is a perfect number
8589869056 is a perfect number
137438691328 is a perfect number
2305843008139952128 is a perfect number
2658455991569831744654692615953842176 is a perfect number
191561942608236107294793378084303638130997321548169216 is a perfect number
13164036458569648337239753460458722910223472318386943117783728128 is a perfect number
14474011154664524427946373126085988481573677491474835889066354349131199152128 is a perfect number
You can get a significant improvement by not searching for perfect numbers but rather search for Mersenne primes, and then output their companion perfect number:
def lucas_lehmer_test(p):
s = 4
m = 2 ** p - 1
for _ in range(p - 2):
s = ((s * s) - 2) % m
return s == 0
def is_odd_prime(number):
"""
Efficiency of this doesn't matter as we're only using it to
test primeness of exponents not the mersenne primes themselves
"""
i = 3
while i * i <= number:
if number % i == 0:
return False
i += 2
return True
print(6) # to simplify code, treat first perfect number as a special case
for n in range(3, 5000, 2): # generate up to M20, found in 1961
if is_odd_prime(n) and lucas_lehmer_test(n):
print(2**(n - 1) * (2**n - 1))
Not perfect, but far better than trying to factor numbers looking for perfect ones.