views:

84

answers:

5

So I've got this snippet of code. And it works (It says 1 is non-prime).:

n = 1
s = 'prime'
for i in range(2, n / 2 + 1):
    if n == 1 or n % i == 0:
        s= 'non-' +s
        break

print s

My problem is that if I change the fourth line to: if n % i == 0 or n == 1:, it doesn't work (it says 1 is prime.)

Why is that? Since I'm using or should it be that either one of them is True so the order doesn't count?

(I'm still learning about boolean so I may be making some basic mistake.)

Thanks in advance!

EDIT: Thanks for the answers; I never realized my issue with the range() function. And about the code working and not working: I have no idea what happened. I may have made some mistake somewhere along the way (maybe forgot to save before running the script. Although I could've sworn that it worked differently :P ). Maybe I'm just getting tired...

Thanks for the answers anyways!

+1  A: 

i think the problem is when n is 1, the loop is skipped.

Dyno Fu
+2  A: 

In both cases, the body of the loop does not run, because when 'n' is 1, it does not fall within the range of (n,n/2+1)

The code you posted says that 1 is prime (again, because the loop body does not execute at all)

Triptych
+1  A: 

The precedence is fine. % is evaluated first, then ==, then or, so it breaks down into:

     ___or___
    /        \
  ==          ==
 /  \        /  \
n    1      %    0
           / \
          n   i

Your problem is that your for loop is not being executed at all, so that s is still set to "prime".

The range 2,n/2+1 when n is 1 equates to 2,1 which will result in the body not being executed.

In fact it won't be executed where n is 2 either since 2/2+1 is 2 and the range 2,2 doesn't execute. The values are the start and terminating value, not start and end (last) value - it's just fortuitous there that 2 is considered a prime by virtue of the initialisation of s :-)

Try this instead:

#!usr/bin/python

n = 9
s = 'prime'
if n == 1:
    s = 'non-prime'
else:
    i = 2
    while i * i <= n:
        if n % i == 0:
            s= 'non-prime'
            break
        i = i + 1
print s

It's wasteful going all the way up to n/2, the square root of n is all that's needed.

paxdiablo
I really didn't notice that :(... But I have no idea what happened. I swear something worked differently before but I guess I'm just getting tired and I made some mistake somewhere along the way. Thanks for the answer though!
vlad003
+1  A: 

Other answers already correctly addressed your specific problem (which, in other words, is that the loop executes only if n/2 + 1 > 2, that is, n/2 > 1, which means n > 2 with new-style division [[python 3 or suitable imports from the future or flags...]], n > 3 with classic style truncating division).

Wrt the specific question you posed:

Since I'm using or should it be that either one of them is True so the order doesn't count?

The order does count because or (like and) is a short-circuiting operator: specifically, or is guaranteed to go left to right, and stop if the left operand is true (because it does not need to know about the right one). This doesn't matter for your specific code, but it's crucial in cases such as, e.g.:

if i == 0 or n / i > 3: ...

If or wasn't going left-to-right (and stopping ASAP), the right-hand operand might get executed even when i equals 0 -- but then the division would raise an exception! With Python's rules, this code snippet won't raise exceptions (if i is an int, at least;-).

Again: this has nothing to do with the specific problem you're having (see other answers and the start of this one), but it's important for you to know for the future, so, since you asked, I took the opportunity to explain!-)

Alex Martelli
Thanks for the info on or. I figured it worked that way since I noticed that happening in some other code, but I never realized the importance of that fact. But I guess if there's no exceptions that could be raised, the ordering shouldn't matter; it would still give the needed result. I've still got lots to learn about this :-)
vlad003
@vlad003 The ordering __can__ matter very much in code that depends on it. For example, `a = x or initialize_x()`. Although this is bad style, you do see it around and it is important to understand it when you do. If `x` evaluates to true, then it is equivalent to `a = x`. otherwise, it is equivalent to `x = initialize_x()`. If you change the order, then `initialize_x()` gets called every time.
aaronasterling
A: 
for n in range(101):
    s = 'prime'
    if n < 2 or not (n & 1): ## not(n & 1) == is even number (last bit 0) == not (n % 2) 
        s = 'non-'+s
    else:
        for i in range(3, int(n**0.5) + 1,2):
             if not(n % i):
                 s= 'non-' +s
                 break
    print "%i is %s" % (n,s)

You need not check all even numbers and you can stop the check at square root of n.

Tony Veijalainen
instead of striding 2 in the inner range, you can start at 6 and stride 6 checking i-1 and i+1. you have to check 3 manually this way.
aaronasterling
Thought it was over complicated, usually only drop even numbers from consideration. Otherwise I would consider n % 30 valid modulos for primes.
Tony Veijalainen