我的Python代码上的MemoryError,如何优化?

时间:2014-06-15 09:48:05

标签: python python-2.7

我对CodeEval提出了挑战,即:

  

序列011212201220200112 ...构造如下:首先是0,然后重复以下动作:已写入的部分归于右边,替换为0到1,1对2,2到0.等等。

     

0 -> 01 -> 0112 -> 01121220 -> ...

     

创建一个算法,确定序列中第N个位置的数字。

INPUT SAMPLE:

0
5
101
25684

输出样本:

0
2
1
0

约束:

0 <= N <= 3000000000

我已经编写了代码,但是从约束中,N可以是一个非常大的数字,我的代码在MemoryError

上得到numstring += temp

这是我的代码:

from string import maketrans

def predict(n):
    n = int(n)
    instring = '012'
    outstring = '120'
    tab = maketrans(instring, outstring)
    numstring = '0'
    temp = numstring
    while len(numstring) <= n:
        temp = temp.translate(tab)
        numstring += temp
        temp = numstring
    print numstring[n]

有没有办法优化这个?

注意:输入和输出是字符串类型,而不是整数

3 个答案:

答案 0 :(得分:1)

想一想。实际上是否有必要存储所有内容?

你从一个角色开始;这会生成两个,生成四个,等等。给定子字符串中的每个字符都是前一个子字符串中的一个,只有一个字符的函数。你不需要跟踪任何其他角色;只有影响你答案的那些。

答案 1 :(得分:1)

这种问题很容易通过递归思考解决。首先,你必须摆脱你正在处理字符串和字符串操作的想法。 您必须执行的翻译是0 -> 11 -> 22 -> 0。这意味着您只需将1添加到模3的所有数字(即添加1,除以3并取余数)。

递归地如何解决这个问题?好吧,如果请求的N0,那么你就是字符串开头的基本情况,所以答案是0。 如果请求是针对更大的索引,我们该怎么办? 汤姆指出,如果你把序列分成两半,那么下半场的每个角色都是上半场中一个角色的函数。如果我们可以计算这样一个字符的索引,我们可以递归地解决问题。

很容易计算出这样一个字符的索引。序列本身具有无限长度,但是,如果输入N,您始终可以考虑长度为2^(ceil(log2(N)))的前缀,并且您总是会遇到如下情况:

a b c ... z | A B C ... Z
         middle   ^
                  N 

即。其中N位于下半场。

如果从N我们减去前半部分的长度,我们获得所需字符的索引。字符串的一半长度为2^(floor(log2(N)))

   a b c ... z A B C ... Z      ~~>      A B C ... Z
              ^    ^                         ^
             2^x   N                        N - 2^x

所以我们必须递归地解决N - 2^x(带x = floor(log2(N)))的问题,然后我们必须应用转换来获得相对于我们在下半部分的事实的结果字符串。

换句话说,解决方案是:

def find_digit(n):
    if n == 0:
        return 0
    # bit_length() gives floor(log2(N))
    length = n.bit_length()
    return (1 + find_digit(n - 2 ** (length-1))) % 3

事实上:

In [22]: find_digit(0)
Out[22]: 0

In [23]: find_digit(5)
Out[23]: 2

In [24]: find_digit(101)
Out[24]: 1

In [25]: find_digit(25684)
Out[25]: 0

In [26]: find_digit(3000000000)
Out[26]: 0

In [27]: find_digit(3000000001)
Out[27]: 1

In [28]: find_digit(3000000002)
Out[28]: 1

In [29]: find_digit(30000000021265672555626541643155343185826435812641)
Out[29]: 2


In [30]: find_digit(30000000021265672555626541643155343185826435812641**10)
Out[30]: 1

请注意,此解决方案具有内存和计算效率,仅执行log(n)操作。但是它仍然限于1000个递归调用。

从递归解决方案中,很容易获得非递归解决方案,该解决方案可用于大量输入:

def find_digit(n):
    start = 0
    while n:
        start += 1
        length = n.bit_length()
        n -= 2 ** (length - 1)
    return start % 3

如果您想输出字符串,只需将return行更改为return str(start % 3)即可。 如果您想接收字符串输入,只需在函数顶部添加n = int(n)

答案 2 :(得分:0)

如果你考虑字符串的大小,我会假设这个练习的重点是将整个字符串存储在内存中。

另外,您可能想尝试替换

numstring += temp
temp = numstring

通过

temp = "%s%s" % (numstring, temp)

这可能会在短期内提高内存效率,即使它无法真正解决潜在的算法问题。