优化Python代码

2 投票
4 回答
1196 浏览
提问于 2025-04-16 10:18

有没有什么建议可以让这个Python代码在寻找下一个回文数时更快呢?

输入的数字可以有1000000位。

已添加评论

#! /usr/bin/python
    def inc(lst,lng):#this function first extract the left half of the string then
                     #convert it to int then increment it then reconvert it to string
                     #then reverse  it and finally append it to the left half. 
                     #lst is input number and lng is its length
        if(lng%2==0):

            olst=lst[:lng/2]
            l=int(lng/2)
            olst=int(olst)
            olst+=1
            olst=str(olst)
            p=len(olst)
            if l<p:
                olst2=olst[p-2::-1]
            else:
                olst2=olst[::-1]
            lst=olst+olst2
            return lst
        else:
            olst=lst[:lng/2+1]
            l=int(lng/2+1)
            olst=int(olst)
            olst+=1
            olst=str(olst)
            p=len(olst)
            if l<p:
                olst2=olst[p-3::-1]
            else:
                olst2=olst[p-2::-1]
            lst=olst+olst2
            return lst



    t=raw_input()
    t=int(t)

    while True:
        if t>0:
            t-=1
        else:
            break

        num=raw_input()#this is input number
        lng=len(num)
        lst=num[:]

        if(lng%2==0):#this if find next palindrome to num variable
                     #without incrementing the middle digit and store it in lst.

            olst=lst[:lng/2]
            olst2=olst[::-1]
            lst=olst+olst2

        else:
            olst=lst[:lng/2+1]
            olst2=olst[len(olst)-2::-1]
            lst=olst+olst2

        if int(num)>=int(lst):#chk if lst satisfies criteria for next palindrome
            num=inc(num,lng)#otherwise call inc function
            print num
        else:
            print lst

4 个回答

0

你不需要去找回文数,其实可以直接生成一个。

把输入的数字分开,然后把它反转过来。如果生成的数字太小了,就把左边的部分加一,然后再反转一次:

def nextPal(n):
    ns = str(n)
    oddoffset = 0
    if len(ns) % 2 != 0:
        oddoffset = 1

    leftlen = len(ns) / 2 + oddoffset
    lefts = ns[0:leftlen]
    right = lefts[::-1][oddoffset:]
    p = int(lefts + right)
    if p < n:
        ## Need to increment middle digit
        left = int(lefts)
        left += 1
        lefts = str(left)
        right = lefts[::-1][oddoffset:]
        p = int(lefts + right)

    return p

def test(n):
    print n
    p = nextPal(n)
    assert p >= n
    print p

test(1234567890)
test(123456789)
test(999999)
test(999998)
test(888889)
test(8999999)
2

我觉得这段代码大部分时间都花在把字符串转换成整数,然后再转换回来。剩下的时间就是在处理字符串和在Python解释器里来回跳转。针对这三件事,我们能做些什么呢?代码里有一些不必要的转换,我们可以把它们去掉。我觉得字符串切片是没法避免的。为了减少在解释器里的时间,你只需要尽量写少一点代码 :-) 把所有代码放在函数里也会有帮助。

你程序底部的代码,试图快速猜测以避免调用 inc(),里面有一两个bug。以下是我可能会这样写那部分代码:

def nextPal(num):
    lng = len(num)
    guess = num[:lng//2] + num[(lng-1)//2::-1]  # works whether lng is even or odd
    if guess > num:  # don't bother converting to int
        return guess
    else:
        return inc(numstr, n)

这个简单的改动让你的代码在不需要调用 inc 的情况下快了大约100倍,而在需要调用的情况下快了大约3倍。

想要更快,我觉得你需要完全避免转换成整数。这意味着要在不使用普通Python整数加法的情况下,手动增加数字的左半部分。你可以使用一个 array,然后手动执行加法算法:

import array

def nextPal(numstr):
    # If we don't need to increment, just reflect the left half and return.
    n = len(numstr)
    h = n//2
    guess = numstr[:n-h] + numstr[h-1::-1]
    if guess > numstr:
        return guess

    # Increment the left half of the number without converting to int.
    a = array.array('b', numstr)
    zero = ord('0')
    ten = ord('9') + 1
    for i in range(n - h - 1, -1, -1):
        d = a[i] + 1
        if d == ten:
            a[i] = zero
        else:
            a[i] = d
            break
    else:
        # The left half was all nines. Carry the 1.
        # Update n and h since the length changed.
        a.insert(0, ord('1'))
        n += 1
        h = n//2

    # Reflect the left half onto the right half.
    a[n-h:] = a[h-1::-1]
    return a.tostring()

对于需要增加的数字,这样做又快了大约9倍。

你可以通过使用 while 循环代替 for i in range(n - h - 1, -1, -1) 来稍微加快速度,再通过让循环同时更新数组的两个半部分,而不仅仅是更新左半部分,然后最后再反射回来,这样又能快大约两倍。

撰写回答