Python文字列をスライスするときの時間はどれくらいですか?Python文字列は不変であることを考えると、O(1)
またはO(n)
スライスの実装方法によって異なります。
(潜在的に大きい)文字列のすべてのサフィックスを反復する関数を記述する必要があります。文字列全体のタプルと文字の読み取りを開始するためのインデックスとしてサフィックスを表すことにより、文字列のスライスを回避できましたが、それは醜いです。代わりに、私は単純に次のように自分の関数を書きます:
_def do_something_on_all_suffixes(big_string):
for i in range(len(big_string)):
suffix = big_string[i:]
some_constant_time_operation(suffix)
_
...時間の複雑さはO(n)
またはO(n2)
になります。ここで、n
はlen(big_string)
ですか?
短い答え:str
スライス、一般的にはコピー。つまり、文字列のn
サフィックスごとにスライスを実行する関数は、O(n2)
の機能を果たしています。つまり、 bytes
sを使用して元のバイトデータのゼロコピービューを取得する を使用してmemoryview
のようなオブジェクトを操作できる場合は、コピーを回避できます。機能させる方法については、以下のゼロコピースライスを実行する方法を参照してください。
長い答え:(C)Python str
データのサブセットのビューを参照してスライスしないでください。 str
スライスには、正確に3つの操作モードがあります。
mystr[:]
_:まったく同じstr
への参照を返します(共有データだけでなく、同じ実際のオブジェクト、_mystr is mystr[:]
_ str
は不変なので、そうすること)mystr[1:1] is mystr[2:2] is ''
_)であり、長さが1の低い序数の文字列もキャッシュされたシングルトンです(CPython 3.5.0では、latin-1で表現できるすべての文字、つまりrange(256)
、キャッシュされます)str
は作成時にコピーされ、その後、元のstr
とは無関係になります。#3が一般的なルールである理由は、大きなstr
がメモリの一部のビューによってメモリに保持される問題を回避するためです。 1 GBのファイルがある場合は、それを読み込んでスライスします(そうです、シークできると無駄になります。これは説明のためです)。
_with open(myfile) as f:
data = f.read()[-1024:]
_
その後、最後の1 KBを表示するビューをサポートするために、メモリに1 GBのデータが保持されますが、これは重大な無駄です。スライスは通常小さめなので、ビューを作成する代わりにスライスをコピーする方がほとんど常に高速です。また、str
の方が簡単な場合もあります。サイズを知る必要がありますが、データへのオフセットも追跡する必要はありません。
Pythonでビューベースのスライスを実行する方法はありあり、Python 2では、str
(str
はPython 2で バッファプロトコル をサポートしているため、バイトのようであるため)。Py2str
およびPy3 bytes
(およびbytearray
、_array.array
_、numpy
配列、_mmap.mmap
_ sなどの他の多くのデータ型)を作成できます。 a memoryview
つまり、元のオブジェクトのゼロコピービュー であり、データをコピーせずにスライスできます。したがって、Py2に使用(またはエンコード)できる場合str
/Py3 bytes
、そしてあなたの関数は任意のbytes
のようなオブジェクトで動作することができます、そしてあなたはそうすることができます:
_def do_something_on_all_suffixes(big_string):
# In Py3, may need to encode as latin-1 or the like
remaining_suffix = memoryview(big_string)
# Rather than explicit loop, just replace view with one shorter view
# on each loop
while remaining_suffix: # Stop when we've sliced to empty view
some_constant_time_operation(remaining_suffix)
remaining_suffix = remaining_suffix[1:]
_
memoryview
sのスライスは、新しいビューオブジェクトを作成します(それらは、表示するデータの量とは無関係に固定サイズで超軽量です)。データはないため、_some_constant_time_operation
_はコピーを格納できます必要に応じて、後でスライスしても変更されません。 Py2 str
/Py3 bytes
として適切なコピーが必要な場合は、.tobytes()
を呼び出して、未加工のbytes
objを取得するか、(Py3ではそれのみ)表示)、それをバッファからコピーするstr
に直接デコードします。例: str(remaining_suffix[10:20], 'latin-1')
。
それはすべて、スライスの大きさに依存します。以下の2つのベンチマークをまとめました。 1つ目は文字列全体をスライスし、2つ目は少しだけスライスします。 このツール を使用した曲線近似により、
# s[1:-1]
y = 0.09 x^2 + 10.66 x - 3.25
# s[1:1000]
y = -0.15 x + 17.13706461
最初のものは、4MBまでの文字列のスライスに対して非常に線形に見えます。これは、2番目の文字列の作成にかかる時間を本当に測定していると思います。 2番目はかなり一定ですが、非常に高速であるため、おそらくそれほど安定していません。
import time
def go(n):
start = time.time()
s = "abcd" * n
for j in xrange(50000):
#benchmark one
a = s[1:-1]
#benchmark two
a = s[1:1000]
end = time.time()
return (end - start) * 1000
for n in range(1000, 100000, 5000):
print n/1000.0, go(n)