首页 文章

快速查找2的幂数位

提问于
浏览
4

任务是搜索2 ^ 10000以下的每个2的幂,返回包含字符串的第一个幂的索引 . 例如,如果要搜索的给定字符串是“7”,则程序将输出15,因为2 ^ 15是其中包含7的第一个幂 .

我通过蛮力尝试来解决这个问题,大约70%的测试用例超时 .

for i in range(1,9999):
    if search in str(2**i):
        print i
        break

如何以5秒的时间限制接近这个?

6 回答

  • 0

    尽量不要在每一步计算 2^i .

    pow = 1
    for i in xrange(1,9999):
        if search in str(pow):
            print i
            break
        pow *= 2
    

    您可以随时计算它 . 这应该可以节省大量的计算时间 .

    使用 xrange 会阻止构建列表,但这可能不会产生太大的影响 .

    in 可能实现为二次字符串搜索算法 . 它可能(或可能不是,你必须测试)更有效地使用像KMP这样的字符串搜索 .

  • 2

    更快的方法是直接以十进制计算数字

    def double(x):
        carry = 0
        for i, v in enumerate(x):
            d = v*2 + carry
            if d > 99999999:
                x[i] = d - 100000000
                carry = 1
            else:
                x[i] = d
                carry = 0
        if carry:
            x.append(carry)
    

    然后搜索功能就可以了

    def p2find(s):
        x = [1]
        for y in xrange(10000):
            if s in str(x[-1])+"".join(("00000000"+str(y))[-8:]
                                       for y in x[::-1][1:]):
                return y
            double(x)
        return None
    

    另请注意,所有2的最大2 ^ 10000的幂的数字仅为15百万,并且搜索静态数据要快得多 . 如果每次都不能重新启动程序

    def p2find(s, digits = []):
        if len(digits) == 0:
            # This precomputation happens only ONCE
            p = 1
            for k in xrange(10000):
                digits.append(str(p))
                p *= 2
        for i, v in enumerate(digits):
            if s in v: return i
        return None
    

    使用这种方法,第一次检查将花费一些时间,接下来的检查将非常快 .

  • 5

    计算每个2的幂,并使用每个字符串构建后缀树 . 这是所有字符串大小的线性时间 . 现在,查找基本上是每个查找字符串长度的线性时间 .

    我认为你不能因为计算复杂性而打败这个 .

  • 0

    只有10000个号码 . 您不需要任何复杂的算法 . 只需提前计算并进行搜索 . 这应该只需要1或2秒 .

    powers_of_2 = [str(1<<i) for i in range(10000)]
    
    def search(s):
        for i in range(len(powers_of_2)):
            if s in powers_of_2[i]:
                return i
    
  • 4

    试试这个

    twos = []
    twoslen = []
    two = 1
    for i in xrange(10000):
        twos.append(two)
        twoslen.append(len(str(two)))
        two *= 2
    
    tens = []
    ten = 1
    for i in xrange(len(str(two))):
        tens.append(ten)
        ten *= 10
    
    s = raw_input()
    l = len(s)
    n = int(s)
    
    for i in xrange(len(twos)):
        for j in xrange(twoslen[i]):
            k = twos[i] / tens[j]
            if k < n: continue
            if (k - n) % tens[l] == 0:
                print i
                exit()
    

    这个想法是预先计算2,10的每个幂,并且还预先计算每个2的幂的位数 . 这样,问题就减少到找到存在aj的最小i,使得在删除最后的j之后来自2 **的数字我得到一个以n结尾的数字或表示为公式(2 ** i / 10 ** j - n)%10 ** len(str(n))== 0 .

  • 2

    这里的一个大问题是将二进制整数转换为十进制表示法需要时间二次的位数(至少以Python的直接方式) . 伪造自己的十进制算术实际上更快,正如@ 6502在他的回答中所做的那样 .

    但是让它的 decimal 模块更快地运行它 - 至少在Python 3.3.2下(我不知道之前在Python decimal 版本中内置了多少C加速) . 这是代码:

    class S:
        def __init__(self):
            import decimal
            decimal.getcontext().prec = 4000  # way more than enough for 2**10000
            p2 = decimal.Decimal(1)
            full = []
            for i in range(10000):
                s = "%s<%s>" % (p2, i)
                ##assert s == "%s<%s>" % (str(2**i), i)
                full.append(s)
                p2 *= 2
            self.full = "".join(full)
    
        def find(self, s):
            import re
            pat = s + "[^<>]*<(\d+)>"
            m = re.search(pat, self.full)
            if m:
                return int(m.group(1))
            else:
                print(s, "not found!")
    

    和样品用法:

    >>> s = S()
    >>> s.find("1")
    0
    >>> s.find("2")
    1
    >>> s.find("3")
    5
    >>> s.find("65")
    16
    >>> s.find("7")
    15
    >>> s.find("00000")
    1491
    >>> s.find("666")
    157
    >>> s.find("666666")
    2269
    >>> s.find("66666666")
    66666666 not found!
    

    s.full 是一个超过1500万个字符的字符串 . 它看起来像这样:

    >>> print(s.full[:20], "...", s.full[-20:])
    1<0>2<1>4<2>8<3>16<4 ... 52396298354688<9999>
    

    因此,字符串包含2的每个幂,指数跟随尖括号括起来 . find() 方法构造一个正则表达式来搜索所需的子字符串,然后向前看以找到电源 .

    玩弄这个,我确信只有 any 搜索方式是"fast enough" . 它获得了大部分时间吸收的大国的十进制表示 . 并且 decimal 模块解决了那个问题 .

相关问题