I have made some code that calculates prime numbers (Nothing special I know) and as expected it slows down the bigger the number, I know it is impossible to make it the same speed no matter the number but I am sure I could improve it's speed, I just don't know how...
import time
number = 1000000001
count = 0
start = time.time()
while number > 1000000000 and number < 10000000000000:
for i in range(1, round(number/2 + 1)):
if (number / i).is_integer():
count += 1
if count > 1:
break
if count < 2:
print(str(number) + ' prime')
number = number + 1
count = 0
end = time.time()
print(end - start)
Several things:
you do not have to check from 1, but 3, 5 and all odd numbers;
you do not have to check until n/2, but you can stop at sqrt(n);
do not use division, since then you work with floating points, but use % to check modulo;
you only have to check odd numbers (or two); and
you can omit the greater than check in the while loop, since the number only increments.
So an improved version would be:
import time
from math import sqrt # import square root
start = time.time()
for number in range(1000000001,10000000000000,2): # use for, hops of 2
for i in range(3, int(sqrt(number))+1,2): # check 3, 5,... up to sqrt(n)
if number % i == 0: # use modulo checks instead of floating point
break
else: # use a for-else structure for faster ending
print(str(number) + ' prime')
count = 0
end = time.time()
print(end - start)
Nevertheless, Python is not designed to get the most out of a CPU. If you really want to code a superoptimized algorithm, you will have to pick a faster (and less convenient) programming language.
Related
New to Python & the community. Navigating different exercises and I'm having trouble finding the solution. I need to remove the trailing, that follows when using end=','
Any wisdom or guidance is very appreciated!
low = 10000
up = 10050
for num in range(low, up + 1):
if num > 1:
for i in range(2, num):
if (num % i) == 0:
break
else:
print(num,end=',')
It looks like you're trying to print prime numbers in a given range. Arguably, mixing discovery of prime numbers and printing them is what causes the problem. With proper decomposition, this problem won't exist:
def generate_primes(low, up):
for num in range(max(low, 2), up+1):
if all(num % i for i in range(2, num)):
yield num
print(*generate_primes(low, up), sep=',')
As a positive side effect, you can now reuse prime generator in other parts of the program which don't require printing.
Also note that checking all numbers up to num is not necessary - if the number is composite one of the factors will be less or equal to sqrt(num). So, a faster prime generator would be something like:
def generate_primes(low, up):
for num in range(max(low, 2), up+1):
if all(num % i for i in range(2, int(num**0.5 + 1))):
yield num
Try this :
low = 10000
up = 10050
nums = []
for num in range(low, up + 1):
if num > 1:
for i in range(2, num):
if (num % i) == 0:
break
else:
nums.append(str(num))
a = ",".join(nums)
print(a)
Essentially, just add all the elements that will be outputted in a list and then use the join function to convert them into a string and print it.
Solution
import sys
low = 10000
up = 10050
firstValuePrinted = 0
for rootIndex, num in enumerate(range(low, up + 1)):
if num > 1:
for subIndex, i in enumerate(range(2, num)):
if (num % i) == 0:
break
else:
print("", end = "," if firstValuePrinted==1 else "")
firstValuePrinted = 1
print(num, end = "")
sys.stdout.flush()
Explanation
Since we can't determine on which number num the loop will exit we can't exactly know the end condition. But we can figure out the starting number.
So based on that logic, instead of printing the "," at the end we actually print it at the start by using a variable to maintain if we have printed any values or not in the above code that is done by firstValuePrinted.
sys.stdout.flush() is called to ensure the console prints the output right away and does not wait for the program to complete.
Note: Don't forget to import sys at the start of the file.
Benefits when compared to other answers
This type of implementation is useful if you want to output the num variable continuously so as to not have to wait for all the numbers to be calculated before writing the value.
If you put all the values into an array and then print, you actually need large memory for storing all the numbers (if low and up is large) as well as the console will wait till the for loop is completed before executing the print command.
Especially when dealing with prime numbers if the low and up variables are far apart like low=0 and up=1000000 it will take a long time for it to be processed before any output can be printed. While with the above implementation it will be printed as it is being calculated. While also ensuring no additional "," is printed at the end.
I have a code that is supposed to produce the lowest monthly payment you need to make to payoff a balance within a year. It works perfectly for all numbers until (so far as I've tested) 772000000000000.
Here is the code (numbers I've tested are below with their results):
import time
balance = float(input("balance: "))
annualInterestRate = float(input("AIR: "))
# formulas for lower and higher binding for bisectional search
monthlyInterestRate = annualInterestRate / 12
lower = balance / 12
higher = (balance * (1 + monthlyInterestRate) ** 12) / 12
while True:
guess = ((higher - lower) / 2 + lower)
print('higher: %s' % higher)
print('lower: %s' % lower)
remaining = balance
for i in range(12):
unpaid = remaining - guess
remaining = unpaid + monthlyInterestRate*unpaid
if higher - lower <= .01 and remaining < 0:
result = lower
print("Lowest Payment: %s" % result)
break
elif higher - lower <= .01 and remaining >= 0:
result = higher
print("Lowest Payment: %s" % result)
break
elif remaining < -0.01:
higher = guess
print("remaining: %s" % remaining)
print(guess)
print('too high')
time.sleep(.5)
elif remaining > 0:
lower = guess
print("remaining: %s" % remaining)
print(guess)
print('too low')
time.sleep(.5)
As I said, this gives the correct result for every number I tested but then I tested 999999999999999 and I got an infinite loop, I narrowed down where the issues start happening by testing the following values all using .2 as the AIR, using different AIR can produce different but similar results depending on the number, but the following should give you a good idea of what's happening:
662000000000000 works
771999999999999 works
772000000000000 repeats higher and lower over and over again after some time
772100000000000 works
772200000000000 repeats higher and lower over and over again after some time
772300000000000 infinite loop
772400000000000 infinite loop
772500000000000 infinite loop
882100000000000 infinite loop
999999999999999 infinite loop
Feel free to try them yourself, I'm completely dumbfounded why this is happening?
If you use floats you have to consider that these cannot represent all possible decimal values. If the values get big enough the difference between two representable floating point values might just exceed your threshold. That leads to a situation where the bisection cannot progress because there is just no "middle" float between the values. For example with:
balance = float("772300000000000")
annualInterestRate = float("0.2")
It ends up in an infinite loop with:
higher: 70368815315719.6
lower: 70368815315719.58
So, let's examine this a bit:
>>> a = 70368815315719.6
>>> b = 70368815315719.58
>>> import numpy as np
>>> np.nextafter(a, 0) == np.float64(b)
True
>>> np.nextafter(b, np.inf) == np.float64(a)
True
So there's no float between a and b but:
>>> b - a
-0.015625
So this is bigger than your threshold. So nothing can change between loops which results in the infinite loop.
However, you can easily fix this by using an arbitary precision Fraction:
from fractions import Fraction
balance = Fraction("772300000000000")
annualInterestRate = Fraction("0.2")
... # rest of your code
All operations in your code preserve the Fraction (if you had used math functions or ** it could be different) and at least on my computer it eventually finishes with:
higher: 161683724083791631395206486083981108997/2297661589986627614146560
lower: 41391033365450653948925712865241263190149/588201367036576669221519360
Lowest Payment: 161683724083791631395206486083981108997/2297661589986627614146560
Note the / in the output which comes from the Fraction.
I've recently started learning Python. My apologies if this is really obvious.
I am following along with the 2008 MIT open course on Computer Science and am working on the problem of calculating the 1000th prime integer. Python 2.7.3, Win7 lappy (cough, cough...)
Here's the code I came up with:
num = 3
primeList = [2]
while len(primeList) < 1000:
for i in primeList:
if num % i == 0:
break
else:
primeList.append(num)
num += 1
print "The 1,000th PRIME integer is", primeList[999]
One of the assignment conditions was to only check odd numbers. Given the starting num is three, I figured it would be easy enough to simply change num+=1 to num+=2. Of note: I won't bore you with the detailed code I composed, but while writing this I was using a very verbose mode of printing out the results of each check, whether or not it was prime, which number was being checked, which integer divided into it if it wasn't prime & such (again, sorry - newB!)
At this point I became curious to test if this was actually taking less time to compute - seemed like if half the numbers are being checked for primacy, it should take half the time, no?
I imported the time module to check how long this was taking. Computing to the 1000th was pretty quick either way so I increased the number of primes I was searching for to the 10,000th and didn't see any significant difference. between num+=1 & num+=2
import time
start = time.time()
num = 3
primeList = [2]
while len(primeList) < 10000:
for i in primeList:
if num % i == 0:
break
else:
primeList.append(num)
num += 2
print "The 10,000th PRIME integer is", primeList[9999]
end = time.time()
print "That took %.3f seconds" % (end-start)
Sometimes the n+=2 even took a couple milliseconds longer. ?. I thought this was odd and was wondering if someone could help me understand why - or, more to the point: how?
Furthermore, I next imported the sqrt() function thinking this would reduce the number of integers being checked before confirming primacy, but this doubled the runtime =^O.
import time
start = time.time()
from math import sqrt
num = 3
primeList = [2]
while len(primeList) < 100000:
for i in primeList:
if i <= sqrt(num):
if num % i == 0:
break
else:
primeList.append(num)
num += 2
print "The 100,000th PRIME integer is",primeList[99999]
end = time.time()
print 'that took', end - start, "seconds, or", (end-start)/60, "minutes"
Certainly - it might be the way I've written my code! If not, I'm curious what exactly I am invoking here that is taking so long?
Thank you!
Two things.
First, you're calculating sqrt(n) on every loop iteration. This will add work, because it's something else your code now has to do on every pass through the loop.
Second, the way you're using sqrt doesn't reduce the number of numbers it checks, because you don't exit the loop even when i is bigger than sqrt(n). So it keeps doing a do-nothing loop for all the higher numbers.
Try this instead:
while len(primeList) < 100000:
rootN = sqrt(num)
for i in primeList:
if i <= rootN:
if num % i == 0:
break
else:
primeList.append(num)
break
else:
primeList.append(num)
num += 2
This finds 100,000 primes in about 3 seconds on my machine.
Is there any suggestion on solving next higher prime and palindrome number from a given int.
Here is the snippet I am trying but its a kind of slow, please suggest if you ve any good algorithm that i can test.
#!/usr/bin/python
def next_higher(n):
while True:
s = str(n)
if not any([n % i == 0 \
for i in range(2, int(n**0.5))]) and s == s[::-1]:
return n
n = n + 1
print next_higher(2004)
print next_higher(20)
Output:
10201
101
Updated code testing for palindrome before prime. much faster than my previous code.
I am implementing the suggestion from user2357112.
#!/usr/bin/python
def next_higher(n):
while True:
s = str(n)
if s == s[::-1]:
if not any([n % i == 0 \
for i in range(2, int(n**0.5))]):
return n
n = n + 1
print next_higher(2004111)
print next_higher(2004)
print next_higher(2004)
print next_higher(20)
There are quite a few optimizations you could do:
Like user2357.. suggested in the comments, test palindromeness first, and then check if the number is prime, since prime check is more expensive.
You don't need to check even number divisibility once you check is the number is divisible by 2. So you can change it to [2] + range(3, int(n**0.5) + 1, 2) to check only odd numbers after 2. (Also you need to do sqrt + 1 like I mentioned in the comments)
You should use () instead of []. [] generates the entire list of factors first and only then checks for any. If you use (), it creates a generator, so it stops as soon as a True value is found without calculating the entire list.
You should also use xrange instead of range for the same reason (xrange gives a generator, range gives a list)
You can use the Sieve of Eratosthenes algorithm to significantly reduce the time taken for prime number check.
You can also see if the palindrome check can be made faster. You can actually skip a whole lot of numbers instead of doing just + 1 each time.
Here is a version with most of these optimizations except the last two:
def next_higher(n):
if n % 2 == 0:
n = n - 1
while True:
n = n + 2
s = str(n)
if s == s[::-1]:
if not any((n % i == 0 for i in xrange(3, int(n**0.5) + 1, 2))):
return n
This should be pretty fast for your needs I believe. But you can do the last 2 optimizations to make it much more faster if you want.
Other than what has already been suggested,
What I suggest is that you first get the first palindrome number that is just higher than the given integer.
You can do this by trying to match the centre digits outwards.
Also, you should only check for numbers with odd number of digits, since if a number has even number of digits and it is a palindrome, then it will always be divisible by 11 and cannot be prime.
Once you get the first palindrome number that has odd number of digits and that is just higher than the current number, test it for primality and find the next palindrome number higher than this one.
You can do this by incrementing the centre digit.
Keep doing this till it rolls over to zero. In that case start incrementing the two neighbouring digits.
Continue, till you reach a prime number.
I tried optimizing the palindrome check i.e to find odd palindrome's.
Since the first digit should be odd number, i focused on that part.
Here's the code below with the assumptions its greater than 1 digit.
def next_odd_palindrome(n):
"""to check the next odd palindrome number"""
if n%2==0:
n=n-1
while True:
n=n+2
s = str(n)
if int(s[0])%2==0:
n = int(str(int(s[0])+1)+ s[1:])
s = str(n)
if s==s[::-1]:
return n
let me know if anything wrong.
Just for the fun of it, I implemented all optimizations by Hari Shankar and Abhishek Bansal.
It first finds the higher odd length palindrome, then increment the palindrome in a way that keeps its palindromity. Then checks each number using prime numbers calculated by Sieve method in the beginning.
This can process up to n=10^14 (can be higher if you increase the CACHE size) under 1 second in my computer =D
primes = []
CACHE = int(10**7) # Cache size for Sieve
# Custom class for immediate printing of output
import sys
class Unbuf:
def __init__(self,stream):
self.stream = stream
def write(self,data):
self.stream.write(data)
self.stream.flush()
sys.stdout = Unbuf(sys.stdout)
def sieve():
global primes
is_prime = [False,False]+([True]*(CACHE-1))
for i in xrange(2,int(CACHE**0.5)):
if is_prime[i]:
is_prime[i*i::i] = [False]*((CACHE-i*i+i)/i)
primes = [num for num, bool_prime in enumerate(is_prime) if bool_prime]
def is_prime(n):
"""Checks whether n is prime"""
global primes
if n<2:
return False
if n==2:
return True
for prime in primes:
if prime>n**0.5+1:
return True
if n%prime==0:
return False
# For the case that the number is bigger than the square of our largest prime
for num in xrange(primes[-1]+2,n**0.5+1,2):
if n%num==0:
return False
return True
def next_higher_odd_length_palindrome(n):
n = str(n)
if len(n)%2==0: # Even length, take the smallest odd length (10(00)*1)
n = '1'+('0'*(len(n)-1))+'1'
else:
middle_idx = len(n)/2
left = int(n[:middle_idx+1])
left_cmp = n[middle_idx::-1]
right_cmp = n[middle_idx:]
# If mirroring left part to right part
# makes the number smaller or equal, then
if right_cmp>=left_cmp:
# Increase the left half number
left = left+1
# Mirror left part to the right part
n = str(left)+str(left)[-2::-1]
return n
def next_higher(n):
if n<=1:
return 2
# Ensure the number is a palindrome of odd length
n = next_higher_odd_length_palindrome(n)
while True:
if is_prime(int(n)):
return int(n)
n = next_higher_odd_length_palindrome(n)
if int(n[0])%2==0:
new_lead = str(int(n[0])+1)
n = new_lead+n[1:-1]+new_lead
import time
print 'Sieving...',
start_time = time.time()
sieve()
print 'Done in %.3fs' % (time.time() - start_time)
print next_higher(2004111)
print next_higher(2004)
print next_higher(20)
while True:
n = int(raw_input('Enter n: '))
start_time = time.time()
result = next_higher(n)
print 'Next higher prime palindrome: %d (calculated in %.3fs)' % (result, time.time() - start_time)
Which in my computer gives this output:
Sieving... Done in 1.444s
3007003
10301
101
Enter n: 1999999999
Next higher prime palindrome: 10000500001 (calculated in 0.004s)
Enter n: 1999999999999
Next higher prime palindrome: 3000002000003 (calculated in 0.051s)
Enter n: 1000000000000
Next higher prime palindrome: 1000008000001 (calculated in 0.030s)
Enter n:
I am now doing the MIT opencourse thing, and already the second assignment, I feel it has left me out in the cold. http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/assignments/pset1a.pdf
The specifics of it, are to write something that can calculate the 1000th prime number. We only know about the print, ==, =, 1=,if, else, elif, while, %, -,+,*,/, commands I think. We also don't yet know about importing libraries.
My Idea of how it would work is to take an odd number and try to divide it by, 3,4,5,6,7,8,9 and if %n !=0, then add a number to NumberofPrimes variable starting with 11 as the base of the tests, and assigning it a base value of 4 at the base of NumberofPrimes, though I don't know if that is even right, because I wouldn't know how to display the 1000th prime number.
Am I close?
The latest incarnation of it is as follows:
##calculate the 1000th prime number
potprime = 3
numberofprime = 1
cycle = if potprime%3 = 0:
break
if potpimre%4 = 0:
break
if potprime%5 = 0:
break
if potprime%6 = 0:
break
if potprime%7 = 0:
break
if potprime%8 = 0:
break
if potprime%9 = 0:
break
numberofprime + 1
potprime + 1
if potprime%2 == 0:
potprime = potprime + 1
if potprime != 0:
cycle
Where exactly am I going wrong? Walk me through it step by step. I really want to learn it, though I feel like I am just being left out in the cold here.
At this point, it would be more beneficial for me to see how a proper one could be done rather than doing this. I have been working for 3 hours and have gotten nowhere with it. If anybody has a solution, I would be more than happy to look at it and try to learn from that.
Looks like I am late
It is quite straight forward that if a number is not divisible by any prime number, then that number is itself a prime number. You can use this fact to minimize number of divisions.
For that you need to maintain a list of prime numbers. And for each number only try to divide with prime numbers already in the list. To optimize further it you can discard all prime numbers more than square root of the number to be tested. You will need to import sqrt() function for that.
For example, if you test on 1001, try to test with 3, 5, 7, 11, 13, 17, 19, 23, 29 and 31. That should be enough. Also never try to find out if an even number is prime. So basically if you test an odd number n, then after that test next number: (n + 2)
Have tested the below code. The 1000th prime number is 7919. Not a big number!!
Code may be like:
from math import sqrt
primeList = [2]
num = 3
isPrime = 1
while len(primeList) < 1000:
sqrtNum = sqrt(num)
# test by dividing with only prime numbers
for primeNumber in primeList:
# skip testing with prime numbers greater than square root of number
if num % primeNumber == 0:
isPrime = 0
break
if primeNumber > sqrtNum:
break
if isPrime == 1:
primeList.append(num)
else:
isPrime = 1
#skip even numbers
num += 2
# print 1000th prime number
print primeList[999]
The following code is gross, but since 1000 is indeed a small index, it solves your problem in a fraction of a second (and it uses only the primitives you are supposed to know so far):
primesFound = 0
number = 1
while primesFound < 1000:
number = number + 1 # start from 2
# test for primality
divisor = 2
numberIsPrime = True
while divisor*divisor <= number: # while divisor <= sqrt(number)
if number % divisor == 0:
numberIsPrime = False
break
divisor = divisor + 1
# found one?
if numberIsPrime:
primesFound = primesFound + 1
print number
You can test the solution here.
Now you should find a more efficient solution, optimize and maybe go for the 1000000-th prime...
For one thing, I'm pretty sure that in Python, if you want to have an if statement that tests whether or not A = B, you need to use the == operator, rather then the =.
For another thing, your algorithm would consider the number 143 to be prime, even though 143 = 11 * 13
You need keep track of all the prime numbers that you have already computed - add them to an array. Use that array to determine whether or not a new number that you are testing is prime.
It seems to me that you are jumping into the deep-end after deciding the kiddy-pool is too deep. The prime number project will be assignment 2 or 3 in most beginning programming classes, just after basic syntax is covered. Rather than help you with the algorithm (there are many good ones out there) I'm going to suggest that you attempt to learn syntax with the python shell before you write long programs, since debugging a line is easier than debugging an entire program. Here is what you wrote in a way that will actually run:
count = 4
n = 10 #I'm starting you at 10 because your method
#says that 2, 3, 5, and 7 are not prime
d = [2, 3, 4, 5, 6, 7, 8, 9] #a list containing the ints you were dividing by
def cycle(n): #This is how you define a function
for i in d: #i will be each value in the list d
if not n%i: #this is equal to if n%i == 0
return 0 #not prime (well, according to this anyway)
return 1 #prime
while count < 1000:
count += cycle(n) #adds the return from cycle to count
n += 1
print n - 1
The answer is still incorrect because that is not how to test for a prime. But knowing a little syntax would at least get you that wrong answer, which is better than a lot of tracebacks.
(Also, I realize lists, for loops, and functions were not in the list of things you say you know.)
Your code for this answer can be condensed merely to this:
prime_count = 1
start_number = 2
number_to_check = 2
while prime_count <= 1000:
result = number_to_check % start_number
if result > 0:
start_number +=1
elif result == 0:
if start_number == number_to_check:
print (number_to_check)
number_to_check +=1
prime_count +=1
start_number =2
else:
number_to_check +=1
start_number = 2
To answer your subsequent question, 'How do I keep track of all the prime numbers?
One way of doing this is to make a list.
primeList = [] # initializes a list
Then, each time you test a number for whether it is prime or not, add that number to primeList
You can do this by using the 'append' function.
primeList.append( potprime ) # adds each prime number to that list
Then you will see the list filling up with numbers so after the first three primes it looks like this:
>>> primeList
[11, 13, 17]
Your math is failing you. A prime number is a number that has 2 divisors: 1 and itself. You are not testing the numbers for primality.
I am very late on this but maybe my answer will be of use to someone. I am doing the same open course at MIT and this is the solution I came up with. It returns the correct 1000th prime and the correct 100,000th prime and various others in between that I have tested. I think this is a correct solution (not the most efficient I am sure but a working solution I think).
#Initialise some variables
candidate = 1
prime_counter = 1
while prime_counter < 1000:
test = 2
candidate = candidate + 2
# While there is a remainder the number is potentially prime.
while candidate%test > 0:
test = test + 1
# No remainder and test = candidate means candidate is prime.
if candidate == test:
prime_counter = prime_counter + 1
print "The 1000th prime is: " + str(candidate)
While I was at it I went on and did the second part of the assignment. The question is posed as follows:
"There is a cute result from number theory that states that for sufficiently large n the product of the primes less than n is less than or equal to e^n and that as n grows, this becomes a tight bound (that is, the ratio of the product of the primes to e^n gets close to 1 as n grows).
Computing a product of a large number of prime numbers can result in a very large number,
which can potentially cause problems with our computation. (We will be talking about how
computers deal with numbers a bit later in the term.) So we can convert the product of a set of primes into a sum of the logarithms of the primes by applying logarithms to both parts of this conjecture. In this case, the conjecture above reduces to the claim that the sum of the
logarithms of all the primes less than n is less than n, and that as n grows, the ratio of this sum to n gets close to 1."
Here is my solution. I print the result for every 1,000th prime up to the 10,000th prime.
from math import *
#Initialise some variables
candidate = 1
prime_counter = 1
sum_logs = log(2)
while prime_counter < 10000:
test = 2
candidate = candidate + 2
# While there is a remainder the number is potentially prime.
while candidate%test > 0:
test = test + 1
# No remainder and test = candidate means candidate is prime.
if candidate == test:
prime_counter = prime_counter + 1
# If the number is prime add its log to the sum of logs.
sum_logs = sum_logs + log(candidate)
if prime_counter%1000 == 0:
# For every 1000th prime print the result.
print sum_logs," ",candidate," ",sum_logs/candidate
print "The 10000th prime is: " + str(candidate)
Cheers,
Adrian
I came up with this solution in my interview, but I didn't get the job :( It has about 1/100 less iterations than the solution above:
from math import *
MAX_IDX=1000
MAX_IDX-=1
num_iter=0
l_pirme_list=[3]
candidate=l_pirme_list[0]
prime_counter=1
while prime_counter < MAX_IDX:
candidate+=2
#Cut the binary number in half. This is quite faster than sqrt()
bin_candidate=format(candidate, "2b")
max_prime_search=int(bin_candidate[:len(bin_candidate)/2+1],2)+1
# max_prime_search=sqrt(candidate)+1
candidate_is_prime=1
for prime_item in l_pirme_list:
num_iter+=1
if candidate % prime_item==0:
candidate_is_prime=0
break
elif prime_item > max_prime_search:
candidate_is_prime=1
break
if candidate_is_prime:
prime_counter+=1
l_pirme_list.append(candidate)
l_pirme_list.insert(0,2)
print "number iterations=", num_iter
print l_pirme_list[MAX_IDX]