Optimising list comparison

Graham Ashton graz at mindless.com
Fri Mar 15 16:17:18 EST 2002


A friend of mine is using Python in a research project where he needs to
compare each element of two vectors, such that comparing [a, b, c] and 
[d, e, f] would involve comparing a with d, b with e and c with f.

The only possible values in either list are 0, 1 and -1. When comparing
two values, if either value is set to -1 then both values are deemed to be
equal. So we've got:

    0 ==  0
    0 == -1
    1 ==  1
    1 == -1
    1 !=  0

What he wants to know is simply whether all the values in the two lists
are "equal", in which case the comparison returns 1, otherwise it returns
0.

Now, having read Skip M's python optimisation page I would have thought
that the following example could be beaten into the ground by an
alternative involving map(). Obviously, a C extension would win the grand
prize, but I'm interested in learning some map() tricks....

    list1 = [1] * 20
    list2 = [1] * 19 + [0]

    def compare():
        for i in range(len(list1)):
            a = list1[i]
            b = list2[i]
            if (a != b) and (a != -1) and (b != -1):
                return 0
        return 1

Neither of us have yet managed to come up with anything to beat it (we've
been timing the alternatives over 100000 itterations).

Any hints? What I can't work out how to do is how to break out of a call
to map() if two values don't match. The only thing I've come up with so 
far is this:

    func = lambda a, b: (a == b) or (a == -1) or (b == -1) or 1 / 0
    try:
        map(func, list1, list2)
    except ZeroDivisionError:
        pass

and though I felt quite smug when I'd thought of it, it's more than a bit
hacky. The raise and except also double the execution time on my machine.

Here's my complete script:

----cut----
#!/usr/bin/env python

import time

list1 = [1] * 20
## list1 = [1] * 9 + [0] + [1] * 10
list2 = [1] * 19 + [0]

def benchmark(func, iters=100000):
    start = time.time()
    for i in range(iters):
        r = func()
    end = time.time()
    print "%d iterations of %s took: %.3f secs" % (iters, func, end - start)

def attempt1():
    for i in range(len(list1)):
        if (list1[i] != list2[i]) and (list1[i] != -1) and (list2[i] != -1):
            return 0
    return 1

func = lambda a, b: (a == b) or (a == -1) or (b == -1) or 1 / 0

def attempt2():
    try:
        map(func, list1, list2)
    except ZeroDivisionError:
        pass

if __name__ == "__main__":
    benchmark(attempt1)
    benchmark(attempt2)
----cut----

Cheers,

Graham.



More information about the Python-list mailing list