I have the following program:
def main():
print "Running"
primes = sieve(100000)
print "Sieve is done"
def sieve(n):
print "starting sieve"
primes = []
times = 0
numbers = range(2, n):
print "sieve array filled"
while len(numbers) > 0:
current = numbers[0]
primes.append(current)
numbers.remove(current)
times = times + 1
if (times % 10 == 0):
print str(times) + "th prime is " + str(current)
# Remove every multiple
for i in numbers:
if (i % current == 0):
numbers.remove(i)
When finding all the primes up to a large number (lets say ten thousand) I wanted to be able to see how far along the program is by looking at the output. So I decided I would print out every tenth prime. However, when printing it out, it waits until the very end of the program to print it. I added in sys.stdout.flush() right after the print statement but it didn't make any difference. I then tried running the script with python -u <file name> and still, no difference at all.
This is what I get as output:
Running
starting sieve
sieve array filled
Then after about a minute the rest of the output is shown at once.
Why can't I turn the buffer off? I'm trying to modify the code as little as possible.
Having tested a few things, I'm not sure that your problem is actually the output buffering, it's just the behaviour of your algorithm. Try printing current near the top of your while loop, and you'll see that the early numbers take a very long time to work through, and then as numbers gets shorter and shorter, each new value of current gets much faster to process and you start seeing the primes pop up.
Try:
while len(numbers) > 0:
current = numbers[0]
print current
primes.append(current)
numbers.remove(current)
The reason this is so slow is the loop where you remove elements from numbers:
# Remove every multiple
for i in numbers:
if (i % current == 0):
numbers.remove(i)
Every time you remove a number, Python has to go and shift all the elements after that number that you removed back one place. Every delete is O(n)*, and you do O(n) deletes, so each iteration of this step takes O(n^2) time.
If you replace this with a list comprehension, then Python builds a new list from the old list -- no moving involved -- which is an O(n) operation. Here's the way I would do that step:
# Remove every multiple
numbers = [i for i in numbers if (i % current) != 0]
With this change, your code runs way faster for me. It's done in under 5 seconds, and there's no output buffering problem.
*There's a nice table of time complexities for Python list operations here.
Try using sys.stdout.write instead of print. This should work better with sys.stdout.flush.
Related
I'm a novice and learning python (using 2.7). I'm attempting some simple scripts to test how python handles different types of loops.
My question is: can python change the starting point of the "range" function on each iteration if the start point is assigned to a variable? Here is an example of my code:
def build(n, period):
n2 = n
for digit in range(int(n2), 20):
print "At the top i is %d" % n
digit += period
numbers.append(digit)
print "Numbers now:", numbers
print "At the bottom i is %d" % digit
print "The Numbers:"
n2 += period
for num in numbers:
print num
key = raw_input("Press 1 to call function \"build\", press any other key to quit.")
if key == "1":
i = raw_input("What integer is our start value?")
amt = raw_input("What is the common difference?")
numbers = [int(i)]
build(int(i),int(amt))
else:
quit()
I tried to use a second local variable 'n2' inside the function so I could keep the initial value of 'n' constant and then redefine the range for each iteration. The very first number in the appended list moves by the common difference but after that it always steps by +1 integer. I can easily make this happen with a 'while' loop but curious if a 'for' loop can be used to accomplish this?
range creates a fixed list the moment you call that function at the beginning of your for loop. You can think of the top of the for loop as assigning n2 to the next element of that list no matter what you do inside the loop. If you want to change the period of the range, use the third argument:
range(n, 20, period)
will move in steps of size period through the range instead of steps of size one.
It won't work in a way you expect. Expression range(int(n2), 20) gets evaluated only one time in the beginning of for-loop. You can't change the scope of a for-loop that way.
What you can modify is a step parameter in range function, but it does not change your starting point - it only defines what is the next element in the iteration process.
I wrote a program to find out the primes in a list of numbers, just so practice formatting and all. Here is my code:
from math import *
#Defining range to be checked
a = range(1,10)
#Opening empty list
b = []
#Wilsons method for primes
for n in a:
if ((factorial(n-1))+1)%n == 0:
b.append(n)
This code runs without issue and fairly fast, at least at this stage of use. However, when I include a while statement(see below), it is substantially slower.
from math import *
#Defining range to be checked
a = range(1,10)
#Opening empty list
b = []
#Wilson't method for primes
for n in a:
while n>1:
if ((factorial(n-1))+1)%n == 0:
b.append(n)
Could anyone explain why that would be the case?
n.b: I know there are more efficient method to find primes. I am just practicing formatting and all, although I don't mind improving my code.
edit: mistaken addition of less than symbol rather than the appropriate greater than symbol. Corrected.
As pointed out by several people your code will result in an infinite loop as the value of n does not change within your while-loop.
You are probably not looking for a while loop in the first place. It should be sufficient to use the for loop without the first iteration (n = 1). If you insist on including n=1, using an if statement is a possible workaround:
a=range(1,10)
b=[]
for n in a:
if n>1:
if ((factorial(n-1))+1)%n == 0:
b.append(n)
I am trying to write a script in python to find the 1000th prime number. I don't understand why this isn't working here. Basically while the mod is less than the square root of the number and still has a remainder, the mod goes up by one. This should continue until the mod equals the square root of the number. Then the check should remain at 0 and the number should be prime. Every time I try to run the script it tells me theres a system error.
import math
b=2
count=2
next_odd=3
next_prime=1
check = 0
while count<=10:
while b<float(math.sqrt(next_odd)):
if next_odd%b>0:
b+=1
if next_odd%b == 0:
check+=1
if check > 0:
next_prime=next_odd
next_odd+=2
print(next_prime)
b=2
count+=1`
I understand what you are trying to do, but unfortunately there were too many things wrong with your program. Here is a working version. I made minimal changes. Hopefully you can compare the below version with your own and see where you went wrong.
import math
count=2
next_odd=3
next_prime=1
while count<=1000:
b=1
check = 0
while b<float(math.sqrt(next_odd)):
b+=1
if next_odd%b == 0:
check+=1
if check == 0:
next_prime=next_odd
print(next_prime)
count+=1
next_odd+=2
With the above program, 1000th prime can be successfully determined to be 7919.
(first, I assume the tick on the end of your code is a typo in your stack overflow post, not the code itself)
Consider what happens when next_odd is prime. This block:
while b<float(math.sqrt(next_odd)):
if next_odd%b>0:
b+=1
if next_odd%b == 0:
check+=1
will increment b up until the square root of next_odd without ever incrementing check. That means that if check > 0: won't pass, and thus count never increments, and you then you just spin around in the
while count<=10:, skipping both if blocks because their conditions are false.
In other words, you don't actually say what to do when next_odd is prime. This is also an example of why while shouldn't really be used when all you want to do is increment through numbers (which is what you're using it for here). Try something like this:
max_num = 10000 # or whatever
for odd in range(3, max_num, 2):
factor_count = 0
for factor in range(2, math.floor(math.sqrt(max_num)) + 1):
if odd % factor == 0:
factor_count += 1
if factor_count == 0:
print(odd)
A couple points about this code:
There's no (non-constant) variables in the global scope. That makes it much easier to reason about how the script's state changes over time.
The use of for-loops over while-loops guarantees that our script won't get caught in an infinite loop due to an erroneous (or unaccounted for) condition.
The use of for-loops means that we don't have to worry about incrementing all of the variables ourselves, which dramatically reduces the amount of state that we have to manage.
Hope that helps!
Oh, note that there are much more efficient ways to compute primes also. See https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
Can someone explain why this code works with the sample number 13195, but crashes when I use the problem's number
num = 13195
def isprime(num):
for i in range(2,num):
if num % i == 0:
ans = i
return ans
print isprime(isprime(isprime(num)))
In Python 2, range constructs a list. So the program is trying to contain an enormous list in memory, and it can't. Use xrange which will generate the numbers on demand for iteration instead of all at once.
You also need to end the loop early or it will spend forever checking so many numbers. So once you find a divisor, use it to divide the original number and make it smaller and thus manageable.
You need to assign a default value to ans.
When the input number is a prime number, the program never assigns anything to the variable ans. So that, when the function tries to return that variable, it is not actually defined.
I'm new to Python, and I'm playing around with recursive functions just for practice.
I made the following algorithm which takes a number as x and halves it until it is equal to 1. n is the number of times x has been halved.
def binary(x, n = 0):
print(n,":",x)
x = x // 2
n += 1
if x > 0:
binary(x, n)
return x
return x
I'm trying to make a loop that will call binary() with multiple values for x. I want my step to be doubled each iteration. I have it working with a while loop like the one below.
i = 1
while i < 1000000000:
print("when x is", i, ":")
binary(i)
i += i
For some reason though, I can't seem to achieve the same thing with a For loop. Here's what I have now.
for i in range(1,1000):
print("when x is", i, ":")
binary(i)
i += i
In the code above, i += i does not seem to effect the i in my header. I know that range() takes a third parameter called step, but I've tried this:
for i in range(1,1000, i += i):
# statements
This gives me a name error, and says "i is not defined".
Most of my experience with programming is in JavaScript and C#. In both of those languages I wouldn't of had any trouble doing this.
How would I get this to work in a For loop using Python 3?
The third parameter of range is indeed step. But you should use it with a computed value like:
for i in range(1,1000,2):
#statements
The reason why your i += i didn't worked is because, under the hood, the for-loop is executing something similar to i = next(...) at the end of an iteration, overiding your last increment.
Edit
You can achieve the desired effect using a generator, but it kinda kills the "trying to avoid while-loops" thing:
def myrange(start, stop):
i = start
while i < stop:
yield i
i += i
for i in myrange(1, 1000):
print(i)
Anyway, while-loops are perfectly valid constructs and I’d personnally go with one in this case. Do not forget that the for-loop has a rather different semantic in python than in both languages you’re used to. So trying to use a for-loop because you are able to do so with javascript seems like a bad idea if all what you need is a while-loop.
range can step by a fixed amount, but not a variable amount. Use a while-loop to increment i by i:
i += i
You could replace the while-loop with an iterator, such as:
import itertools as IT
for i in (2**i for i in IT.count()):
if i >= 1000000000: break
print("when x is", i, ":")
binary(i)
but I don't think this has any advantage over a simple while-loop.
If all you're doing is doubling i, then why not just raise it to the power?
for p in range(int(1000000000**0.5)):
print(binary(2**p)