使用XOR加速Python2嵌套循环

时间:2016-11-02 11:07:20

标签: python performance python-2.7 nested-loops xor

  

The answer这个标记重复的问题是错误的,不能满足我的需要。

我的代码旨在从一系列数字中计算哈希值。

以矩阵的形式理解结构更容易。如果我从29开始有16个数字,结构将是:(开始= 29,长度= 4)

29,30,31,32,
33,34,35,36,
37,38,39,40,
41,42,43,44

给定算法指定散列将是以粗体给出的数字的XOR:

29,30,31,32, //,
33,34,35, //,36,
37,38, //,39,40,
41, //,42,43,41

哈希= 29^30^31^32^33^34^35^37^38^39 = 54

我的代码是:

def answer(start, length):
    val=0
    c=0
    for i in range(length):
        for j in range(length):
            if j < length-i:
                val^=start+c
            c+=1
    return val

计算像answer(2000000000,10**4)这样的大值所需的时间太多了。

约束:

  • Py2.7.6
  • 除了bz2,crypt,fcntl,mmap,pwd,pyexpat,select,signal,termios,thread,time,unicodedata,zipimport,zlib之外,只有标准库。
  • 计算时间有限。

目前计算测试参数(我不知道)给我一个超时错误。

如何为更大的值提高代码的速度?

3 个答案:

答案 0 :(得分:4)

这是一个已修复的版本,还有一个l测试,以验证它是否提供与天真算法相同的结果。

assert

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) start的范围内,lengthgen_nums快5倍,但我们可以再次快两倍(即大致)通过消除这些函数调用,速度是answer的10倍:

answer

正如Mirek Opoka在评论中提到的那样,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] ^ (0, start - 1, 1, start, 0)[start % 4] start += length return ans 等同于% 4,并且它更快,因为按位算术比执行整数除法和丢弃商更快。所以我们可以用

替换核心步骤
& 3

答案 1 :(得分:1)

看起来您可以替换内部循环,如果使用:

for j in range(length - i) val^=start+c c+=1 c+=i 当我变大时,这应该可以节省一些时间

我担心我现在无法对此进行测试,抱歉!

答案 2 :(得分:1)

我担心,如果您在answer(2000000000,10**4)中输入,您将永远无法“及时”完成。

通过改进内循环,每次更新c变量并使用xrange代替range,您可以获得非常显着的加速,如下所示:

def answer(start, length):
    val=0
    c=0
    for i in range(length):
        for j in range(length):
            if j < length-i:
                val^=start+c
            c+=1
    return val


def answer_fast(start, length):
    val = 0
    c = 0
    for i in xrange(length):
        for j in xrange(length - i):
            if j < length - i:
                val ^= start + c + j
        c += length
    return val


# print answer(10, 20000)
print answer_fast(10, 20000)

分析器显示answer_fast的速度大约是其两倍:

> python -m cProfile script.py
366359392
        20004 function calls in 46.696 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000   46.696   46.696 script.py:1(<module>)
        1   44.357   44.357   46.696   46.696 script.py:1(answer)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
    20001    2.339    0.000    2.339    0.000 {range}

> python -m cProfile script.py
366359392
        3 function calls in 26.274 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000   26.274   26.274 script.py:1(<module>)
        1   26.274   26.274   26.274   26.274 script.py:12(answer_fast)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

但是如果你想要主要的加速(大订单)你应该考虑在Cython中重写你的功能。

以下是它的“cythonized”版本:

def answer(int start, int length):
    cdef int val = 0, c = 0, i, j
    for i in xrange(length):
        for j in xrange(length - i):
            if j < length - i:
                val ^= start + c + j
        c += length
    return val

使用与上述输入参数相同的输入参数,所需的时间不到200毫秒20秒,这是100倍的加速。

> ipython

In [1]: import pyximport; pyximport.install()
Out[1]: (None, <pyximport.pyximport.PyxImporter at 0x7f3fed983150>)

In [2]: import script2

In [3]: timeit script2.answer(10, 20000)
10 loops, best of 3: 188 ms per loop

使用输入参数,需要58ms:

In [5]: timeit script2.answer(2000000000,10**4)
10 loops, best of 3: 58.2 ms per loop