Project Euler #3, infinite loop on factorization - python

So I'm doing Project Euler because dear gods do I need to practice writing code, and also my math skills are rusty as something very rusty. Thusly; Project Euler. I'm sure most here have already seen or heard of the problem, but I'll put it here just for completeness:
The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?
For this, I've written two functions:
from math import sqrt
def isprime(n):
if n == 1:
return False
elif n == 2:
return True
elif n % 2 == 0:
return False
for x in range(3, round(sqrt(n))+1, 2):
if n % x == 0:
return False
else:
return True
This just checks any fed number for primality. It's working as intended (as far as I know), but now that I've said that I grow unsure. Anyway, it checks for special cases first: 1 (never prime), 2 (prime) or if it's divisible by 2 (not prime). If none of the special cases happen, it runs a general primality test.
This is my factorization code:
def factorization(n):
factor = 2
x = 3
while True:
if n % x == 0:
if isprime(x):
factor = x
n = n // x
if n == 1:
return factor
else:
return factor
x += 2
And this is definitely not working as intended. It is, sadly, working for the particular value of the Project Euler problem, but it doesn't work for, say, 100. I'm unsure what I need to do to fix this: what happens is that if it's a number like 100, it will correctly find the first 5 (2*2*5), but after that will loop around and set x = 7, which will make the entire thing loop infinitely because the answer is 2*2*5*5. Would recursion help here? I tried it, but it didn't get any prettier (it would still go into an endless loop for some numbers). I'm unsure how to solve this now.

You're on a good track, but you need to take account of the possibility of repeating factors. You can do that with something like this:
factors = []
while num % 2 == 0:
factors.append(2)
num /= 2
The idea here being that you're going to continue adding 2's to the factor list until the number you're testing becomes odd. You can use similar logic for other factors as well to enhance your factorization method.

I think you have made the problem more complicated than necessary
Here is some pseudo code that you should be able to turn into Python code
from itertools import count
n=600851475143
for x in count(2):
while x divides n:
divide n by x
if n==1:
print x # largest factor will be the last one
break

For the repeating (odd) factors, just increment x when a divisor has not been found:
def factorization(n):
factor = 2
x = 3
while True:
if n % x == 0:
if isprime(x):
factor = x
n = n // x
if n == 1:
return factor
else:
return factor
else:
x += 2
OTOS, it seems that you miss always the "2" factors. Stick them on top and then do the main loop
EDIT (after comment)
You can do a much simpler:
def factorization(n):
factors = []
x = 2
while True:
while n % x == 0:
factors.push(x)
n /= x
if n == 1:
return factors
if x == 2:
x = 3
else:
x += 2

Here is another optimized solution:
import math
def find_prime(num):
if num <= 1:
return False
elif(num == 2):
return True
elif( num % 2 == 0):
return False
for i in range(3, int(math.sqrt(num))+1, 2):
if num%i == 0:
return False
return True
def prime_factor(number):
pf = number;
divList = [];
for i in range(2, int(math.sqrt(number))):
if number % i == 0 :
divList.append(i)
for n in divList:
if(find_prime(n)):
pf = n;
return pf
num = 600851475143
print("Max prime factor :", prime_factor(num))

Related

Python 3. How do you assign a variable in a function to use in the outer scope of the function?

I am extremely new to programming. I have been working on a project where the user is asked to import a number, which goes through a mathematical series. The output is then put into a function to find the factors of the number. From there I am trying to find the factors that are prime numbers?
This is what i have so far.
enter code here####################################
n = int(input("Enter the n value"))
num = sum(10**x for x in range(n))
print("S",n,"is", num)
#####################################
# Factors
#function name nfactors
def nfactors(x):
# This function takes a number and prints the factors
print("The factors of",x,"are:")
for i in range(1, x + 1):
if x % i == 0:
print(i)
fact = nfactors(num)
print(fact)
#####################################
print('The prime numbers are:')
if fact > 1:
# check for factors
for i in range(2,fact):
if (fact % i) == 0:
break
else:
print(fact)
I know this is bad programming but I am trying to learn through doing this project. How can I then take the factors I received as the output of the function and find which factors are prime numbers. I cannot figure out how to name a variable inside the function and use that outside the function, I don't know if this is even possible. If you need any clarifications please let me know. Thanks for any help.
def nfactors(x):
# This function takes a number and prints the factors
print("The factors of",x,"are:")
for i in range(1, x + 1):
if x % i == 0:
print(i)
return i
fact = nfactors(num)
Use the return keyword to return the value of i or whatever it is you would like to use outside of the function!
I seriously hope that you are trying to find the factors of n instead of num as finding the factors of num for a mere number n=12 is a great deal and it'll take several minutes or even hours even with my optimized code.
Anyway assuming you want to find it for n and not num the below code should do your job.
In case you really want to find factors for num, this code will work fine for that too but it'll take too much time. Just change factors = factors(n) to factors = factors(num).
from math import sqrt
#Block 1 (Correct)
n = int(input("Enter the n value"))
num = sum(10**x for x in range(n))
print("S",n,"is", num)
#---------------------------------------------------
def factors_(x):
# This function takes a number and prints the factors
print("The factors of",x,"are:")
list_of_factors = []
for i in range(1, x + 1):
if x % i == 0:
list_of_factors.append(i)
return(list_of_factors) #This returns a list of factors of a given number x
factors = factors_(n) #I am assuming you want to find the prime factors of n instead of num
print(factors)
#------------------------------------------------------------------------------------
def is_prime(num): #This function returns True(1) if the number is prime else Flase(0)
if num == 2:return 1
if num%2 == 0:return 0
i = 3
while i < int(round(sqrt(num)) + 1):
if num % i == 0:return 0
i += 2
return 1
#---------------------------------------------------------------------------------------
prime_factors = []
for i in factors:
if is_prime(i):
prime_factors.append(i)
print("The prime factors of the numbers are:")
print(prime_factors)

Finding a prime number

The problem is that you need to find the prime number after the number input, or if the number input is prime, return that. It works fine. It's just not working when the input is print(brute_prime(1000)). It returns 1001 not 1009. The full code is this:
def brute_prime(n):
for i in range(2, int(n**(0.5))):
if n % i == 0:
n += 1
else:
return n
As Barmar suggests, you need to restart the loop each time you increment n. Your range also ends earlier than it should, as a range stops just before the second argument.
def brute_prime(n):
while True:
for i in range(2, int(n**(0.5)) + 1):
if n % i == 0:
break
else:
return n
n = n+1
remember 2 is a prime number. again you can just check the division by 2 and skip all the even number division
def brute_prime(n):
while True:
if n==2:return n
elif n%2 ==0 or any(n % i==0 for i in range(3, int(n**(0.5)+1),2)):
n += 1
else:
return n
You're not restarting the for i loop when you discover that a number is not prime and go to the next number. This has two problems: you don't check whether the next number is a multiple of any of the factors that you checked earlier, and you also don't increase the end of the range to int(n ** 0.5) with the new value of n.
def brute_prime(n):
while true:
prime = true
for i in range(2, int(n ** 0.5)+1):
if n % i == 0:
prime = false
break
if prime:
return n
n += 1
break will exit the for loop, and while true: will restart it after n has been incremented.
as mention by Chris Martin the wise solution is define a isPrime function separately and use it to get your desire number.
for example like this
def isPrime(n):
#put here your favorite primality test
from itertools import count
def nextPrime(n):
if isPrime(n):
return n
n += 1 if n%2==0 else 2
for x in count(n,2):
if isPrime(x):
return x
if the given number is not prime, with n += 1 if n%2==0 else 2 it move to the next odd number and with count check every odd number from that point forward.
for isPrime trial division is fine for small numbers, but if you want to use it with bigger numbers I recommend the Miller-Rabin test (deterministic version) or the Baille-PSW test. You can find a python implementation of both version of the Miller test here: http://rosettacode.org/wiki/Miller%E2%80%93Rabin_primality_test#Python

How to write a recursive function for palindromic primes?

I have been trying to write a Python program which uses a recursive function to find the palindromic primes between two integers supplied as input. Example of palindromic prime: 313
I already know how to write a recursive function for palindromes, but I am struggling with this one a lot. I would appreciate any help. Thanks
recursive function for palindromes
Presumably to do the palindrome check recursively you check the outer characters:
def is_pali(s):
if len(s) <= 1:
return True
else:
return s[0] == s[-1] and is_pali(s[1:-1])
Now you can iterate over the numbers and see which are palindromes:
[i for i in range(n, m) if is_pali(str(i))]
Since 30000 is the limit, this works (101101 is the smallest number it gets wrong):
>>> [n for n in range(2, 500) if str(n) == str(n)[::-1] and (2**n-1)%n == 1]
[2, 3, 5, 7, 11, 101, 131, 151, 181, 191, 313, 353, 373, 383]
You can of course also use the (2**n-1)%n == 1 primality test in your own recursive palindrome function that you already have.
http://en.wikipedia.org/wiki/Fermat_primality_test
Probably you already went through this idea but here's what I would do...
If you have a palindrome function like this one:
def palindrome(word):
if len(word) == 1 or (len(word) == 2 and word[0] == word[1]):
return True
else:
if word[0] == word[len(word)-1]:
return palindrome(word[1] + word[len(word)-2])
else:
return False
And let's say you have a function to figure out if a number is prime (this I take from here):
def is_prime(number):
if number > 1:
if number == 2:
return True
if number % 2 == 0:
return False
for current in range(3, int(math.sqrt(number) + 1), 2):
if number % current == 0:
return False
return True
return False
You can just call the validation when you find out if your number is a palindrome (casting it to str first).
The missing part is generate the combination of the two integers you might get but well, that's a simple one.
Hope this helps.
-Edit:
Adding a recursive function for getting primes:
def prime(number,limit = 1):
if limit == number:
return True
else:
if number % limit == 0 and limit != 1:
return False
else:
return prime(number, limit + 1)
Instead of recursive solution, what about using more effective list slicing?
def isPalindrome(number):
nstr = str(number)
return nstr == nstr[::-1]
This works by converting the number to string and comparing it's reversed counterpart. There is also known algorithm for determining the palindrome,
using global variable:
sum = 0
def is_palindrome(number):
return palindrome_sum(number) == number
def palindrome_sum(number):
global sum
if number != 0:
remainder = number % 10
sum = sum * 10 + remainder
palindrome_sum(number / 10) * 10 + remainder
return sum
For mathematical recursive function without global variable, this algorithm can be used:
import math
def is_palindrome(number):
return palindrome_sum(number) == number
def palindrome_sum(number, sum=0):
iters = 0
if number != 0:
iters = int(math.log10(number))
remainder = number % 10
sum = palindrome_sum(number / 10) + remainder * 10 ** iters
return sum
It uses the length of number to find it's position in the resulting number. The length can be computed by int(math.log10(number)).
This solution uses the Sieve of Eratosthenes to find the prime numbers less than n. It then uses a basic palindrome check which of those prime numbers are palindromes. The check avoids converting the ints to strs, which is a time consuming operation.
#!/usr/bin/env python2.7
def primeslt(n):
"""Finds all primes less than n"""
if n < 3:
return []
A = [True] * n
A[0], A[1] = False, False
for i in range(2, int(n**0.5)+1):
if A[i]:
j = i**2
while j < n:
A[j] = False
j += i
return (num for num in xrange(n) if A[num])
def is_palindrome(n):
digits = []
while n > 0:
digits.append(n%10)
n /= 10
return digits == digits[::-1]
def main():
while True:
try:
i = int(raw_input("Palindromic primes less than... "))
break
except ValueError:
print "Input must be an integer."
print filter(is_palindrome, primeslt(i))
if __name__ == '__main__':
main()
If you have any questions about how this code works, feel free to ask me by commenting on my answer. Best of luck!

Trying to find the prime numbers using Python

Below is my code to find the prime number using Python which is not working. Here the function prime will take an integer as input and return whether its a prime number or not. Could you please sort out the problem and explain it.
def prime(x):
if x == 0 or 1:
return False
elif x == 2:
return True
else:
for n in range(2, x):
if x % n == 0:
return False
else:
return True
I think i have sorted out the first issue, the first "if" statement should be if x == 0 or x == 1. Now what about the rest.
What does your for loop?
if x % n == 0:
return False
else:
return True
which by the way eqals return bool(x % n)
So, you return in first iteration, when n == 2.
The whole for loop equals return bool(x % 2), which simply checks if x is diviseable by 2.
That's not what you want.
So, what do you want?
You want to check if x is not diviseable by any numer from range(2, x).
You know that x is not prime, if you find one n from range(2, x), for which x % n == 0 is True.
You know that x is prime, when there is no n in range(2, x), for which x % n == 0 is True.
When can you say that none of n from range is a divisor of x?
After checking all ns from range!
After is the key here.
After the loop, in which you try to find divisor, you can only tell that x is prime.
I hope you now understand the code others posted without explanation.
Note: alternative syntax
Code others posted is correct. However, in Python there is second way writing the for, using for .. else:
for x in range(2, x):
if x % n == 0:
return False
else:
return True
The problem is that the return true should not happen until the for loop has completed.
what we have in the original code is a cuple of tests for trivial cases (x less than 3)
and then a loop for testing all the larger numbers.
In the loop an attempt is made to divide x by a smaller number (starting with 2) and then if it divides evenly False is returned, if it does not True is returned, that is the mistake, instead of returning true, the loop should be allowed to repeat, and division should be tried again with the next number, and only after the supply of divisors (from the for loop) has been exhausted should true be returned.
here's a fixed version:
def prime(x):
if x <= 1:
return False
elif x == 2:
return True
else:
for n in range(2, x):
if x % n == 0:
return False
return True
Others have commented that the loop need not continue all the way up to x and that stopping at sqrt(x) is sufficient, they are correct. doing that will make it faster in almost all cases.
Another speed up can be had if you have a list of small primes (upto sqrt(x)) - you need only test divisibility by the primes below sqrt(x),and not every integer in that range.
The below code is for finding the prime number from 2 to nth number.
For an example, the below code will print the prime number from 2 to 50 and also it will print the number in between 2 to 5o which is not prime.
import time
i=2
j=2
count=0
while(i<50):
while (i>j):
if (i%j)==0:
count=count+1
j=j+1
else:
j=j+1
if count==0:
print i," is a prime"
else:
print i," is not a prime"
i=i+1
j=2
count=0
time.sleep(2)

Prime factorization: doesn't work with large numbers?

I'm trying to find the largest prime factor for a number. The code runs correctly on IDLE when used with smaller numbers, but doesn't seem to print anything to the screen at all when I assign a larger number like 600851475143 to n. Why?
def isPrime(n):
isPrime = True
for i in range(2,n-1):
if n % i == 0:
isPrime = False
return isPrime
largest = 0
n = 600851475143
for i in range(2,n-1):
if isPrime(i) and n % i == 0:
largest = i
n = n / i
continue
print("The largest prime factor is", largest)
I'm running Python 3.3, by the way.
==============================================================================
Thanks everyone!
I fixed my original code as follows:
def isPrime(n):
for i in range(2,n-1):
if n % i == 0:
return False
return True
largest = 0
n = 600851475143
for i in range(2,n-1):
if isPrime(i) and n % i == 0:
largest = i
if i == n:
break
n = n / i
print("The largest prime factor is", largest)
Like nakedfanatic said, their code runs faster, and I edited it slightly:
largest = 0
n = 600851475143
i = 2
while True:
if n % i == 0:
largest = i
if n == i:
# finished
break
n = n / i
else:
i += 1
print("The largest prime factor is", largest)
There are several areas of optimization:
all factorization only needs to got up to sqrt(n) (inclusive)
convert isPrime() to a table lookup
Initialize a lookup table using n, then you compute all primes < sqrt(n) only once and loop through them.
As comments pointed out, this takes up large memory space. We can use bit flag to cut the memory requirement to 1/8, and we can cut it by a further half if we skip all the even numbers (then have to test if n is even separately). But that may still be daunting for LARGE n.
(if using current code) return early in isPrime() (by #justhalf)
loop backwards (from sqrt(n) to 2) when looking up the largest factor
return early if the quotient is 1 after dividing by a factor (by #justhalf)
This post (suggested by #prashant) contains more complicated algorithm (making my suggestion very naive ><):
Fastest way to list all primes below N
... (edits are welcome)
It's because you keep trying even if n is already 1.
This code will help you to see the problem:
def isPrime(n):
for i in xrange(2,n-1):
if n % i == 0:
return False
return True
largest = 0
n = 600851475143
for i in xrange(2,n-1):
print 'Checking whether %d divides %d' % (i,n)
if isPrime(i) and n % i == 0:
largest = i
n = n / i
continue
print("The largest prime factor is", largest)
which will produce:
...
Checking whether 6857 divides 6857
Checking whether 6858 divides 1
Checking whether 6859 divides 1
Checking whether 6860 divides 1
Checking whether 6861 divides 1
Checking whether 6862 divides 1
Checking whether 6863 divides 1
Checking whether 6864 divides 1
Checking whether 6865 divides 1
Checking whether 6866 divides 1
Checking whether 6867 divides 1
Checking whether 6868 divides 1
Checking whether 6869 divides 1
Checking whether 6870 divides 1
Checking whether 6871 divides 1
Checking whether 6872 divides 1
Checking whether 6873 divides 1
...
You should break the loop when n becomes 1, so that it won't do unnecessary checking
n = n / i
if n==1:
break
continue
And anyway, your code might be improved by a lot, haha, see others' suggestions.
Most likely, your code isn't terminating with large n, simply because it takes so long to run through the loop.
Your code is running in O(n²) time, which means it will quickly become unreasonably slow as the size of n increases. That is why your algorithm works for small values, but hangs for large values.
This code does the same thing in O(n) time without doing any prime checking at all, and returns a result instantly:
prime_factors = []
n = 600851475143
i = 2
while True:
if n % i == 0:
prime_factors.append(i)
if n == i:
# finished
break
n = n / i
else:
i += 1
print("The largest prime factor is", prime_factors[-1])
More difficult problems may require a different algorithm.
Check this question out: Fastest way to list all primes below N
Your code looks okay, but could take a long time for a large n. Leveraging math can enable you to do this problem orders of magnitude faster.
On that link, I recommend rwh_primes1 for a pure python solution, and primesfrom3to as one that uses numpy. Both of those implementations are fairly short, relatively clear, and do basically the same thing. Those code snippets are written in Python 2, so a translation might look like this:
def rwh_primes1(n):
sieve = [True] * (n//2)
for i in range(3, int(n**0.5)+1,2):
if sieve[i//2]:
sieve[i*i//2::i] = [False] * ((n-i*i-1)//(2*i)+1)
return [2] + [2*i+1 for i in range(1,n//2) if sieve[i]]
isPrime = True
for i in range(2,n-1):
if n % i == 0:
isPrime = False
return isPrime
This loop always exits the first iteration due to the unconditional return. Try:
for i in range(2,n-1):
if n % i == 0:
return False
return True
Also the upper bound n-1 can be reduced to sqrt(n)+1.
Another aspect of your code which may be slowing it down is the second half of your code
largest = 0
n = 600851475143
for i in range(2,n-1):
if isPrime(i) and n % i == 0:
largest = i
n = n / i
continue
Specifically the statement
if isPrime(i) and n % i == 0:
Per the documentation, the second condition is only evaluated if the first one is True. In your case it would make more sense to reverse the conditions so that computationally les expensive division is performed always and the more expensive isPrime() is only called for the actual factors
largest = 0
n = 600851475143
for i in range(2,n-1):
if n % i == 0 and isPrime(i):
largest = i
n = n / i
if n == 1:
break

Categories