Improve pandigital algorithm First of all, I am a beginner in Python trying to learn optimizations and proper coding standards.

Here is a snippet of code that checks if a number follows the rule: first AND last N digits contain all unique numbers from 1 to N, but in any order. This also means that in either first N or last N digits, each number appears exactly once.

import time

start_time = time.time()


def is_pandigital(nr, n):
    digits = ''.join(map(str, range(1, n + 1)))
    nr = str(nr)
    for i in digits:
        if str(i) not in nr[0:9]:
            return False
        if str(i) not in nr[-9:]:
            return False

    return True

assert is_pandigital(1423, 4) is True
assert is_pandigital(1423, 5) is False
assert is_pandigital(14235554123, 4) is True
assert is_pandigital(14235552222, 4) is False # !important
assert is_pandigital(1444, 4) is False
assert is_pandigital(123564987, 9) is True

pandigitals = []
# this loop is strictly for benchmarking is_pandigital
for i in range(100000, 999999):
    if is_pandigital(i, 6):
        pandigitals.append(i)

print pandigitals

print time.time() - start_time, "seconds"


When running this, the result is:

[123456, .......]
2.968 seconds

Process finished with exit code 0


The code seems to work fine, but it doesn't appear to be very efficient. Would you have any tips to improve it?  Any piece of code and/or idea would be highly appreciated.

PS: I chose this loop so that any improvements to the is_pandigital function would be immediately obvious.

Example of numbers I am looking for:

321XXXXXXXXXXXXXXX132 - good

321XXXXXXXXXXXXXXX133 - not good, because the last 3 digits dont contain 1, 2 and 3

231 - good
You can rewrite your asserts without comparing to True and False:

assert is_pandigital(1423, 4)
assert not is_pandigital(1423, 5)
assert is_pandigital(14235554123, 4)
assert not is_pandigital(14235552222, 4) # !important
assert not is_pandigital(1444, 4)
assert is_pandigital(123564987, 9)


You can rewrite your benchmark with list comprehension:

# this loop is strictly for benchmarking is_pandigital
pandigitals = [i for i in range(100000, 999999) if is_pandigital(i, 6)]


Now for the algorithm itself, I must confess that I have troubles understanding what you want to do as you seem to be calling "pandigital" two different things. In any case, I have the feeling that something is wrong in your code, it seems like :

   if str(i) not in nr[0:9]:
        return False
    if str(i) not in nr[-9:]:
        return False


should be 

    if str(i) not in nr[0:n]:
        return False
    if str(i) not in nr[-n:]:
        return False


and

assert not is_pandigital(9999912399999, 3)


should provide you some hints.

I'll go deeper in the code once you confirm that my understanding is correct.

Edit : I have to go, no time to run benchmarks but here are the improvements. I kept different versions to that you can take ideas out of it.

def is_pandigital(nr, n):
    nr = str(nr)
    beg=nr[0:n]
    end=nr[-n:]
    for i in map(str, range(1, n + 1)):
        if i not in beg or i not in end:
            return False
    return True

def is_pandigital(nr, n):
    nr = str(nr)
    beg=set(nr[0:n])
    end=set(nr[-n:])
    return beg==end and beg==set(map(str, range(1, n + 1)))