繁体   English   中英

快速查找2的幂数位

[英]Finding digits in powers of 2 fast

任务是搜索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秒的时间限制接近这个?

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

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

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

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

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

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

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

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

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

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

只有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

尝试这个

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。

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

但它是非常快很多,让Python的decimal模块做到这一点-至少在Python的3.3.2(我不知道有多少C语言加速内置于Python的decimal版本之前)。 这是代码:

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()方法构造一个正则表达式来搜索所需的子字符串,然后向前看以找到电源。

玩弄这个,我确信几乎任何搜索方式都“足够快”。 它获得了大部分时间吸收的大国的十进制表示。 decimal模块解决了这个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM