接尾辞木を使用せず、文字列照合アルゴリズム(KMPなど)を使用するO(n)時間ソリューション(実際には2n + r、nはタプルの長さ、rはサブタプル)があると思います。 -棚)。
次のあまり知られていない定理を使用します。
If x,y are strings over some alphabet,
then xy = yx if and only if x = z^k and y = z^l for some string z and integers k,l.
私は今、私たちの問題の目的のために、これは私たちがする必要があるのは、与えられたタプル/リスト(または文字列)がそれ自体の循環シフトであるかどうかを判断することだけであることを意味すると主張します!
文字列がそれ自体の循環シフトであるかどうかを判断するために、文字列をそれ自体と連結し(実際の連結である必要はなく、仮想の連結で十分です)、部分文字列の一致をチェックします(それ自体と)。
その証拠として、文字列がそれ自体の循環シフトであると仮定します。
与えられた文字列y=uv=vuがあります。uv = vuであるため、上記の定理からu = z^kおよびv=z ^ l、したがってy = z ^ {k+l}である必要があります。他の方向は簡単に証明できます。
これがPythonコードです。この方法はpowercheckと呼ばれます。
def powercheck(lst):
count = 0
position = 0
for pos in KnuthMorrisPratt(double(lst), lst):
count += 1
position = pos
if count == 2:
break
return lst[:position]
def double(lst):
for i in range(1,3):
for elem in lst:
yield elem
def main():
print powercheck([1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1])
if __name__ == "__main__":
main()
そして、これが私が使用したKMPコードです(David Eppsteinによる)。
# Knuth-Morris-Pratt string matching
# David Eppstein, UC Irvine, 1 Mar 2002
def KnuthMorrisPratt(text, pattern):
'''Yields all starting positions of copies of the pattern in the text.
Calling conventions are similar to string.find, but its arguments can be
lists or iterators, not just strings, it returns all matches, not just
the first one, and it does not need the whole text in memory at once.
Whenever it yields, it will have read the text exactly up to and including
the match that caused the yield.'''
# allow indexing into pattern and protect against change during yield
pattern = list(pattern)
# build table of shift amounts
shifts = [1] * (len(pattern) + 1)
shift = 1
for pos in range(len(pattern)):
while shift <= pos and pattern[pos] != pattern[pos-shift]:
shift += shifts[pos-shift]
shifts[pos+1] = shift
# do the actual search
startPos = 0
matchLen = 0
for c in text:
while matchLen == len(pattern) or \
matchLen >= 0 and pattern[matchLen] != c:
startPos += shifts[matchLen]
matchLen -= shifts[matchLen]
matchLen += 1
if matchLen == len(pattern):
yield startPos
サンプルの場合、この出力
[1,0,1,1]
予想通り。
これをshx2のコード(numpyのコードではない)と比較しました。ランダムな50ビットの文字列を生成し、複製して全長を100万にしました。これが出力でした(10進数はtime.time()の出力です)
1362988461.75
(50, [1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1])
1362988465.96
50 [1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1]
1362988487.14
上記の方法は約4秒かかりましたが、shx2の方法は約21秒かかりました。
これがタイミングコードです。(shx2のメソッドはpowercheck2と呼ばれていました)。
def rand_bitstring(n):
rand = random.SystemRandom()
lst = []
for j in range(1, n+1):
r = rand.randint(1,2)
if r == 2:
lst.append(0)
else:
lst.append(1)
return lst
def main():
lst = rand_bitstring(50)*200000
print time.time()
print powercheck(lst)
print time.time()
powercheck2(lst)
print time.time()