4

I have this pseudocode for a Miller-Rabin primality tester:

function isPrime(n, k=5)
    if n < 2 then return False
    for p in [2,3,5,7,11,13,17,19,23,29]
        if n % p == 0 then return n == p
    s, d = 0, n-1
    while d % 2 == 0
        s, d = s+1, d/2
    for i from 0 to k
        x = powerMod(randint(2, n-1), d, n)
        if x == 1 or x == n-1 then next i
        for r from 1 to s
            x = (x * x) % n
            if x == 1 then return False
            if x == n-1 then next i
        return False
    return True

But translating that to Python is hard because of the next i statement in the inner for loop, which must break two loops. There is no goto in Python. Other questioners who have asked this question on Stack Overflow have been told to use a local function with a return, or a try/except condition, or an additional boolean flag, but those solutions either don't apply here or would greatly uglify this lovely pseudocode.

What is the Pythonic approach to this problem?

3
  • 2
    @Christian "I have this pseudocode" This is not supposed to be valid python. Commented Jun 11, 2014 at 18:15
  • 1
    Are you against the for: else paradigm? That might work for you, but if "not ugly" is your criteria you might not want that either. Commented Jun 11, 2014 at 18:16
  • You might want to clarify that you don't want to break out of two loops, but to break out of the first one and continue the second one. (For breaking out of both, the mentioned return trick would do it) Commented Jun 11, 2014 at 18:23

2 Answers 2

3

I think the pythonic approach would be try/except, readability would prefer a method or a boolean, but I think that this is solvable by adding a single line:

    for i in xrange(k):
        x = powerMod(randint(2, n-1), d, n)
        if x == 1 or x == n-1: continue
        for r in xrange(1,s):
            x = (x * x) % n
            if x == 1: return False
            if x == n-1: break #*
        if x != n-1: #added line
            return False
    return True

breaking on the line marked with #* is problematic because it returns false, but if we fix that it's just like "next i".

Another solution, suggested by tobias_k, is to use for/else:

    for i in xrange(k):
        x = powerMod(randint(2, n-1), d, n)
        if x == 1 or x == n-1: continue
        for r in xrange(1,s):
            x = (x * x) % n
            if x == 1: return False
            if x == n-1: break 
        else: #added line
            return False
    return True

return False statement would not be called if the loop was break-ed - only if it was exhausted.

Sign up to request clarification or add additional context in comments.

4 Comments

I think this is one of those cases where you can use for/else instead of that new if
Not at all! That's why I posted that comment! ;-) BTW, I think powerMod is just pow in Python (with optional third parameter. At least the test seems to work that way.
I went with minor formatting, so left it as is. Personally I think that if xxx: yyyy lines are less readable, so I wouldn't use that as well, but I wanted to change the original code as little as possible.
I don't like the first approach, because of the duplicate test. I do like the second approach, which is very idiomatic. Thanks Tal and Tobias!
1

You can use break and continue witn a for: else.

for i from 0 to k
    x = powerMod(randint(2, n-1), d, n)
    # Use 'continue' to go to next i (skip inner loop).
    if x == 1 or x == n-1 then next i
    for r from 1 to s
        x = (x * x) % n
        if x == 1 then return False
        # Use 'break' to exit this loop and go to next i
        # since this loop is at the end of the i loop.
        if x == n-1 then next i
    else:
        # This is only reached if no `break` occurred
        return False
return True

3 Comments

if you break on the second next i you return False when you shouldn't.
@TalKremerman How is that? The return statement will break out of the loop if it is reached, so that won't matter.
if x == n-1 you're supposed to go to the next i. If you simply break, you reach return False and you're not supposed to.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.