13

標準のpython(2.7が望ましい)のみを使用して、特定の文字列内のすべての文字のASCII値を合計するより効率的な方法を探しています。

現在私は持っています:

print sum(ord(ch) for ch in text)

この質問の私の主な焦点と側面は、私が上に書いたことであることを強調したいと思います.

以下は、この質問の重要な側面ではなく、そのように扱われるべきです:

では、なぜ私はそれを求めているのですか?! このアプローチと、ここでPyInline を使用して同じことを行う単純な C コード関数の埋め込みを比較しました。単純な C 埋め込み関数は 17 倍高速であるようです。

私が提案したもの (標準の Python のみを使用) よりも高速な Python アプローチがない場合、Python 開発者がそのような実装をコアに追加していないのは奇妙に思えます。

提案された回答の現在の結果。Windows 7、i-7、Python 2.7:

 text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
 sum(ord(ch) for ch in text)
 >> 0.00521324663262
 sum(array.array("B", text))
 >> 0.0010040770317
 sum(map(ord, text ))
 >> 0.00427160369234
 sum(bytearray(text))
 >> 0.000864669402933

 C-code embedded:
 >> 0.000272828426841
4

4 に答える 4

21

中間体を使用しbytearrayて速度を上げることができます。

>>> sum(bytearray("abcdefgh"))
804

これはジェネレーターよりも 17 倍高速ではありません — 中間体の作成が含まれ、bytearrayPythonsum整数オブジェクトを反復処理する必要があります — しかし、私のマシンでは、8 文字の文字列の合計を 2μs から約 700ns に高速化します。この大まかなタイミングがユースケースにとって依然として非効率的である場合は、アプリケーションの速度が重要な部分をとにかく C で作成する必要があります。

文字列が十分に大きく、 を使用できる場合はnumpy、 を使用して文字列のバッファを直接参照することで、一時コピーの作成を回避できますnumpy.frombuffer

>>> import numpy as np
>>> np.frombuffer("abcdefgh", "uint8").sum()
804

小さい文字列の場合、numpy のビュー作成機構が複雑なため、これは一時配列よりも遅くなります。ただし、文字列が十分に大きい場合、このfrombufferアプローチは効果を発揮し始めます。もちろん、生成されるゴミは常に少なくなります。私のマシンでは、カットオフ ポイントは約 200 文字の文字列サイズです。

また、Guido の古典的なエッセイPython Optimization Anecdoteも参照してください。その特定の手法のいくつかは今では時代遅れになっている可能性がありますが、Python の最適化についてどのように考えるかという一般的な教訓は依然として非常に重要です。


timeitモジュールを使用して、さまざまなアプローチの時間を計ることができます。

$ python -m timeit -s 's = "a" * 20' 'sum(ord(ch) for ch in s)' 
100000 loops, best of 3: 3.85 usec per loop
$ python -m timeit -s 's = "a" * 20' 'sum(bytearray(s))'
1000000 loops, best of 3: 1.05 usec per loop
$ python -m timeit -s 'from numpy import frombuffer; s = "a" * 20' \
                      'frombuffer(s, "uint8").sum()' 
100000 loops, best of 3: 4.8 usec per loop
于 2012-09-19T09:51:00.507 に答える
11

ジェネレーターの作成を削除することで、少し高速化できます(〜40%程度ですが、ネイティブCほど高速ではありません)...

それ以外の:

sum(ord(c) for c in string)

行う:

sum(map(ord, string))

タイミング:

>>> timeit.timeit(stmt="sum(map(ord, 'abcdefgh'))")
# TP: 1.5709713941578798
# JC: 1.425781011581421
>>> timeit.timeit(stmt="sum(ord(c) for c in 'abcdefgh')")
# TP: 1.7807035140629637
# JC: 1.9981679916381836
于 2012-09-19T09:57:28.060 に答える