Memory error while calculating largest prime factor using python - python

I am trying to find out largest prime number of a big number, when I run this I run into an error saying:
Traceback (most recent call last):
File "prb3.py", line 45, in
print prime_factor()
File "prb3.py", line 12, in prime_factor
for n in range(2,i):
MemoryError
It works fine when I run with small number like 13195
"""
Problem:
The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?
"""
import math
def prime_factor():
i=600851475143
factors=[] #list stores all the factors of a number
for n in range(2,i):
if(i%n==0 and (n%2!=0 and n!=2)):
factors.append(n)
"""
Now I have all the factors(which are not even numbers)
Next step is to find prime number from factors list
"""
for factor in factors:
sqr_root=int(math.sqrt(factor))
"""
I take a factor from list and divide it by numbers from 3
to sqroot(factor-1).If I get a 0 as remainder I consider it
as non prime and remove from the list.I apply this only to
factors whose sqr root is greater than 3.If it is less than
3 I divide it by each number between 3 and factor-1.
"""
if(sqr_root<=3):
for num in range(3,factor-1):
if(factor%num==0):
factors.remove(factor)
break
else:
for num in range(3,sqr_root):
if(factor%num==0):
1,1 Top
return len(factors)
if __name__ == "__main__":
print prime_factor()

In Python2, range() returns a list. In your case the list would contain 600851475141 int objects. Since the list is so big, it can't fit in your memory so you get that memory error
Since you don't really need all those numbers in memory at the same time, you could try using xrange() instead.
I think you can simplify your problem by dividing out the factors as you find them. eg.
for n in xrange(2, i):
while(i % n == 0 and (n % 2 != 0 and n != 2)):
i /= n
print n
if i == 1:
break
Not needing to loop 600851475141 times should make your program much faster

Related

Shorter way to test if prime number

To test if a number is prime, I do:
def isprime(n):
if n < 2: return False
for i in range(2, n):
if n % i == 0:
return False
else:
return True
I wonder how to make that more efficient/shorter to write.
With generator:
def isprime(n):
return (all(False for i in range(2,n) if n % i == 0) and not n < 2)
print (isprime(0))
print (isprime(1))
print (isprime(2))
print (isprime(3))
print (isprime(9))
print (isprime(10))
print (isprime(13))
Output:
False
False
True
True
False
False
True
Alongside #Théophile's suggestion, I'd add the following recommendations:
Test whether a number is even and greater than 2 before even calling is_prime (eliminating the need for a function call).
Instead of range(2, n), use range(3, n, 2). This will consider only odd numbers; the third parameters of range is the step by which you'll increment.
Instead of looping through all the integers (or all the odd integers) less than the square root of n, create a cache of the prime numbers you've already found and loop through them. One of the fastest and most elegant ways to do this is using functools.lru_cache, but it will suffice simply to write provide a cache yourself.
Here's a quick and dirty way of doing this that is longer but more efficient than your original proposal:
from math import sqrt
# We seed the cache with the first two odd primes because (1) it's low-
# hanging fruit and (2) we avoid the need to add extra lines of code (that
# will increase execution time) for cases in which the square roots of numbers
# are less than any primes in the list
odd_primes = [3, 5]
def is_prime(n):
# If n is not prime, it must have at least one factor less than its square
# root.
max_prime_factor_limit = int(sqrt(n))
# use a generator here to avoid evaluating all the qualifying values in
# odd_primes until you need them. For example, the square root of 27 is
# 5.1962, but, since it's divisible by 3, you'll only test if 27 is prime
# one time. Not a big deal with smaller integers, but the time to compute
# the next prime will increase pretty fast as there are more potential
# factors.
available_primes = (p for p in odd_primes if p <= max_prime_factor_limit)
for prime in available_primes:
if n % prime == 0:
return False
return True
for i in range(7, 99, 2):
# if no prime factors were found, add to cache
if is_prime(i):
odd_primes.append(i)
print(odd_primes)
There are additional things you can do to speed this up. The one that immediately springs to mind is, instead of calculating the square root of each number you're checking, use the squares of the primes to determine the upper limit of the set of primes you'll check. In other words, if you know that the square of 169 is 13, so you know that any number greater than 169 and less than 289 (the square of 17) will have a prime factor <= 13. You can also use that to save time by calculating the list of prime factors and passing the list to the function you're using to determine if an integer is prime. Note, of course, that this will only work if you're actually creating a list of primes.
number = int(input('please enter a number:'))
if number>1:
for numbers in range(2, number):
if (number % numbers) ==0:
print(f"{number} is not a prime number")
break
else:
print(f"{number} is a prime number")
else:
print(f"{number} is not a prime number")

How does this python loop check for primality, if it loops less than the number n?

Hi guys so I was wondering how is this code:
def is_prime(n):
for i in range(2, int(n**.5 + 1)):
if n % i == 0:
return False
return True
able to check for prime when on line 2: for i in range(2, int(n**.5 + 1)): the range is not : range(2, n)? Shouldn't it have to iterate through every number till n but excluding it? This one is not doing that but somehow it works... Could someone explain why it works please.
The loop iterates on all numbers from 2 to the square root on n. For any divisor it could find above that square root (if it continued iterating to n - 1), there would obviously be another divisor below it.
Because the prime factorisation of any number n (by trial division) needs only check the prime numbers up to sqrt(n)
.. Furthermore, the trial factors need go no further than sqrt(n)
because, if n is divisible by some number p, then n = p × q and
if q were smaller than p, n would have been detected earlier as
being divisible by q or by a prime factor of q.
On a sidenote, trial division is slow to check for primality or possible primality. There are faster probabilistic tests like the Miller-Rabin test which can check quickly if a number is composite or probably prime.

How to optimize and find output for large inputs?

For an input number N, I am trying to find the count of numbers of special pairs (x,y) such that the following conditions hold:
x != y
1 <= N <= 10^50
0 <= x <= N
0 <= y <= N
F(x) + F(y) is prime where F is sum of all digits of the number
finally print the output of the count modulo 1000000007
Sample Input: 2
Sample Output: 2
Explanation:
(0,2) Since F(0)+F(2)=2 which is prime
(1,2) Since F(1)+F(2)=3 which is prime
(2,1) is not considered as (1,2) is same as (2,1)
My code is:
def mod(x,y,p):
res=1
x=x%p
while(y>0):
if((y&1)==1):
res=(res*x)%p
y=y>>1
x=(x*x)%p
return res
def sod(x):
a=str(x)
res=0
for i in range(len(a)):
res+=int(a[i])
return res
def prime(x):
p=0
if(x==1 or x==0):
return False
if(x==2):
return True
else:
for i in range(2,(x//2)+1):
if(x%i==0):
p+=1
if(p==0):
return (True)
else:
return(False)
n=int(input())
res=[]
for i in range (n+1):
for j in range(i,n+1):
if(prime(sod(int(i))+sod(int(j)))):
if([i,j]!=[j,i]):
if([j,i] not in res):
res.append([i,j])
count=len(res)
a=mod(count,1,(10**9)+7)
print(res)
print(a)
I expect the output of 9997260736 to be 671653298 but the error is code execution timed out.
Already posted a bit too long comments, so changing it to answer:
When thinking of such problems, don't translate the problem directly to code but see what can you do only once or in different order.
As of now, you're doing N*N passes, each time calculating a sum of digits for x and y (not that bad) AND factoring each sum to check whether it's prime (really bad). That means for sum s you're checking whether it's prime s+1 times! (for 0+s, 1+(s-1), ..., (s-1)+1, s+0).
What you can do differently?
Let's see what we know:
Sum of digits is the same for many numbers.
Sum of sod(x) and sod(y) is the same for many values.
Number is prime during its 1st and nth check (and checking whether it's prime is costly).
So the best would be to calculate prime numbers only once, and each of the sum only once. But how to do that when we have many numbers?
Change the direction of thinking: get the prime number, split it into two numbers (sodx and sody), then generate x and y from those numbers.
Example:
Prime p = 7. That give possible sums as 0+7, 1+6, 2+5, 3+4.
Then for each sum you can generate a number, e.g. for N=101 and sod=1, you have 1, 10, 100, and for sod=2 you have 2, 11, 20, 101. You can possibly store this, but generating this should not be that bad.
Other optimisation:
You have to think how to limit generating prime numbers using your N:
given N with lenN digits (remember, lenN is ~log(N)), the biggest sum of digits possible is 9*lenN (for N consisting of only 9's). That means our sodx and sody are <= 9*lenN, so prime p = sodx + sody <= 18*lenN
Look: that means 18*lenN checking for whether number is prime vs N*N checks your previous algorithm had!

Python Overflow error: int too large to convert to C long

I'm a beginner and I'm doing the problems in and while doing the third problem, which is about finding the largest prime factor of 600851475143, I get this error:
Python int too large to convert to C long
plist = [2]
def primes(min, max):
if 2 >= min:
yield 2
for i in xrange(3, max, 2):
for p in plist:
if i % p == 0 or p * p > i:
break
if i % p:
plist.append(i)
if i >= min:
yield i
def factors(number):
for prime in primes(2, number):
if number % prime == 0:
number /= prime
yield prime
if number == 1:
break
a = 600851475143
print max(factors(a))
Annoyingly, in Python 2, xrange requires its arguments to fit into a C long. 600851475143 is too big for that on your system. You'll have to rewrite your algorithm to not need such a big range, or use a substitute, such as your own xrange implementation, or a while loop with manual counter management.
This occurs when the number you are dealing with is greater than sys.maxsize
You could possibly use the numpy module and use a larger data type. Not sure how large you need without checking though.

Python code stucks in Iterating although it found the solution

I'm trying to write a python code to find the prime factors of any given number
def pf(n):
for i in range(2,n):
if n%i==0: #find the factors
for j in range(2,i): #check if the factor is prime
if i%j==0:
break
else: #find the prime ones
print(i)
My problem is that this code works fine with small numbers however with big numbers i have to interrupt the execution
for example:
pf(600851475143)
71
839
1471
6857
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
pf(600851475143)
File "<pyshell#1>", line 2, in pf
for i in range(2,n):
KeyboardInterrupt
the prime factors of this big number were found in less than a second, so my question is how to tweak this code to stop unnecessary iterations after finding the factors with the use of the for not the while loop
You can speed things up by dividing n by the obtained value in each iteration step. This way you decrease the number you are iterating. I would implement something like this (not yet sure if this is optimal and results in the lowest number of operations):
from math import sqrt
def pf(n):
if n == 1:
return
if n % 2 == 0:
print(2)
pf(n/2)
return
for i in range(3, int(sqrt(n))+1, 2):
if n % i == 0:
for j in range(3, int(sqrt(i))+1, 2):
if i % j == 0:
break
else:
print(i)
pf(n/i)
return
print(n)
Note, if using the improvement of looping until the root of the number we omit the case that the number itself is a prime number. However, if the function does not result any prime factors it is save to assume that the input is a prime number itself.
The return statements stop the main loop (and the function) after the recursive call. So each call of the function only results in one value and a call for the function on the result of the division of the number by its found prime.
If you make a set with all the prime numbers and check if the value is in this set you will win some time, instead of looping over all values.
Compared to the non-recursive solution by jonrsharpe this one is almost four times as fast:
>>> print timeit.timeit("pf(600851475143)", setup="from __main__ import pf", number=1)
71
839
1471
6857
0.00985789299011
>>> print timeit.timeit("pf2(600851475143)", setup="from __main__ import pf2", number=1)
71
839
1471
6857
0.0450129508972
The implementation is limited by the overflow limit of range(), which results in an overflow for the input value (600851475143**2)+1. More details on the maximum size for range can be found in this question: Python: Range() maximum size; dynamic or static?
A possible issue with this solution could be that the maximum recursion depth is achieved. For more details on that visit this question: Maximum recursion depth
You could try adding prime factors to a list as you find them, and see if they multiply to make the number you are trying to factorize, but I think that might add more time than it would save.
As suggested in the comments, you could also stop at the square root of the number - using for i in range(2, sqrt(n) + 1):.
In terms of generally speeding it up you could also try creating a set of primes, and adding to it when you find them in the 5th line. Example:
if i in primes:
print(i)
else:
for j in range(2,i): # check if the factor is prime
if i%j==0:
break
One further point - use xrange() rather than range(), so you do not internally create the list of all numbers to iterate: (if you are using Python 2 !)
What is the difference between range and xrange functions in Python 2.X?
just iterate square root of value, this is how you can iterate through less nombers and
use generators to skip repeated iteration usinf for else
from math import sqrt
def pf(n):
n = int(sqrt(n))
for i in xrange(2, n): # in python2 use `range` for python3
for j in xrange(2,i):
if i%j == 0:
break
else:
yield i # this will return when prime nomber will found.
print list(pf(4356750))
Here's how I would do it:
from math import sqrt
def pf(n):
"""Print the prime factors of n."""
if n % 2 == 0:
print(2)
for i in range(3, int(sqrt(n))+1, 2):
if n % i == 0: # i is a factor of n
for j in range(3, int(sqrt(i))+1, 2):
if i % j == 0:
break
else: # i is also prime
print(i)
By factoring out the checks for 2 you can almost halve the search space, and using the fact that all prime factors must be below the square root of a number you cut it down even further. This takes about a quarter of a second for 600851475143:
>>> import timeit
>>> timeit.timeit("pf(600851475143)", setup="from __main__ import pf", number=1)
71
839
1471
6857
0.27306951168483806
Another option would be to use a prime sieve to generate all primes below n, then filter out those that are also factors of n (effectively the reverse operation).

Categories