簡體   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