Speed up Python2 nested loops with XOR

后端 未结 3 2111
死守一世寂寞
死守一世寂寞 2020-12-22 12:44

The answer of the question this is marked duplicate of is wrong and does not satisfy my needs.

My code aims to calculate a hash from a seri

3条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-22 13:33

    There is a bug in the accepted answer to Python fast XOR over range algorithm: decrementing l needs to be done before the XOR calculation. Here's a repaired version, along with an assert test to verify that it gives the same result as the naive algorithm.

    def f(a):
        return (a, 1, a + 1, 0)[a % 4]
    
    def getXor(a, b):
        return f(b) ^ f(a-1)
    
    def gen_nums(start, length):
        l = length
        ans = 0
        while l > 0:
            l = l - 1
            ans ^= getXor(start, start + l)
            start += length
        return ans
    
    def answer(start, length):
        c = val = 0
        for i in xrange(length):
            for j in xrange(length - i):
                n = start + c + j
                #print '%d,' % n,
                val ^= n
            #print
            c += length
        return val
    
    for start in xrange(50):
        for length in xrange(100):
            a = answer(start, length)
            b = gen_nums(start, length)
            assert a == b, (start, length, a, b)
    

    Over those ranges of start and length, gen_nums is about 5 times faster than answer, but we can make it roughly twice as fast again (i.e., roughly 10 times as fast as answer) by eliminating those function calls:

    def gen_nums(start, length):
        ans = 0
        for l in xrange(length - 1, -1, -1):
            b = start + l
            ans ^= (b, 1, b + 1, 0)[b % 4] ^ (start - 1, 1, start, 0)[start % 4]
            start += length
        return ans
    

    As Mirek Opoka mentions in the comments, % 4 is equivalent to & 3, and it's faster because bitwise arithmetic is faster than performing integer division and throwing away the quotient. So we can replace the core step with

    ans ^= (b, 1, b + 1, 0)[b & 3] ^ (start - 1, 1, start, 0)[start & 3]
    

提交回复
热议问题