76

sPythonの文字列が 1 文字だけで構成されていることを確認する効率的な方法は何'A'ですか? 次のようall_equal(s, 'A')に動作するようなもの:

all_equal("AAAAA", "A") = True

all_equal("AAAAAAAAAAA", "A") = True

all_equal("AAAAAfAAAAA", "A") = False

一見非効率な 2 つの方法があります。最初に文字列をリストに変換して各要素をチェックするか、2 番目に正規表現を使用します。もっと効率的な方法はありますか、それとも Python でできる最善の方法ですか? ありがとう。

4

8 に答える 8

130

count()これは、その優れたmgilson のタイミング スイートを使用するだけで、はるかに高速で、 よりも数倍高速です。

s == len(s) * s[0]

ここでは、すべてのチェックが Python C コード内で行われます。

  • len(s) 文字を割り当てます。
  • スペースを最初の文字で埋めます。
  • 2 つの文字列を比較します。

紐が長いほど、タイムボーナスが大きくなります。ただし、mgilson が書いているように、文字列のコピーを作成するため、文字列の長さが数百万の記号の場合、問題になる可能性があります。

タイミングの結果からわかるように、一般にタスクを解決する最速の方法は、各シンボルに対して Python コードを実行しません。ただし、set()ソリューションは Python ライブラリの C コード内でもすべてのジョブを実行しますが、おそらく Python オブジェクト インターフェイスを介して文字列を操作するため、まだ遅いです。

UPD:空の文字列のケースについて。それをどうするかは、タスクによって大きく異なります。タスクが「文字列内のすべての記号が同じかどうかを確認する」でs == len(s) * s[0]ある場合、有効な答えです (記号はエラーを意味し、例外は問題ありません)。タスクが「一意のシンボルが 1 つだけ存在するかどうかを確認する」である場合、空の文字列は False を返す必要があり、ブール値を受け取りたい場合、答えはs and s == len(s) * s[0]ですbool(s) and s == len(s) * s[0]。最後に、タスクを「異なる記号がないかどうかを確認する」と理解すると、空の文字列の結果は True になり、答えは になりnot s or s == len(s) * s[0]ます。

于 2013-01-14T15:46:49.850 に答える
47
>>> s = 'AAAAAAAAAAAAAAAAAAA'
>>> s.count(s[0]) == len(s)
True

これは短絡しません。短絡を行うバージョンは次のようになります。

>>> all(x == s[0] for x in s)
True

ただし、最適化された C 実装により、一部の文字列 (サイズなどに応じて) では、おそらく非短絡バージョンの方がパフォーマンスが向上するのではないかと感じています。


timeit投稿された他のオプションのいくつかをテストするための簡単なスクリプトを次に示します。

import timeit
import re

def test_regex(s,regex=re.compile(r'^(.)\1*$')):
    return bool(regex.match(s))

def test_all(s):
    return all(x == s[0] for x in s)

def test_count(s):
    return s.count(s[0]) == len(s)

def test_set(s):
    return len(set(s)) == 1

def test_replace(s):
    return not s.replace(s[0],'')

def test_translate(s):
    return not s.translate(None,s[0])

def test_strmul(s):
    return s == s[0]*len(s)

tests = ('test_all','test_count','test_set','test_replace','test_translate','test_strmul','test_regex')

print "WITH ALL EQUAL"
for test in tests:
    print test, timeit.timeit('%s(s)'%test,'from __main__ import %s; s="AAAAAAAAAAAAAAAAA"'%test)
    if globals()[test]("AAAAAAAAAAAAAAAAA") != True:
        print globals()[test]("AAAAAAAAAAAAAAAAA")
        raise AssertionError

print
print "WITH FIRST NON-EQUAL"
for test in tests:
    print test, timeit.timeit('%s(s)'%test,'from __main__ import %s; s="FAAAAAAAAAAAAAAAA"'%test)
    if globals()[test]("FAAAAAAAAAAAAAAAA") != False:
        print globals()[test]("FAAAAAAAAAAAAAAAA")
        raise AssertionError

str.count私のマシン (OS-X 10.5.8 、core2duo、python2.7.3) では、これらの不自然な (短い) 文字列を使用setしてall、 と をstr.replace少し叩きますが、追い抜かれstr.translatestrmul現在は十分なマージンでリードしています:

WITH ALL EQUAL
test_all 5.83863711357
test_count 0.947771072388
test_set 2.01028490067
test_replace 1.24682998657
test_translate 0.941282987595
test_strmul 0.629556179047
test_regex 2.52913498878

WITH FIRST NON-EQUAL
test_all 2.41147494316
test_count 0.942595005035
test_set 2.00480484962
test_replace 0.960338115692
test_translate 0.924381017685
test_strmul 0.622269153595
test_regex 1.36632800102

タイミングは、異なるシステム間および異なる文字列でわずかに (または大幅に?) 異なる可能性があるため、渡す予定の実際の文字列を調べる価値があります。

最終的に、最適なケースがall十分にヒットし、文字列が十分に長い場合は、それを検討することをお勧めします。それはより良いアルゴリズムです...setソリューションを打ち負かす可能性があるケースは見当たらないので、私はソリューションを避けcountます。

str.translateメモリが問題になる可能性がある場合は、str.replaceを避ける必要がありますstrmul。これらは 2 番目の文字列を作成するためですが、最近では通常、これは問題になりません。

于 2013-01-14T15:04:01.807 に答える
17

セットに変換して、メンバーが 1 つしかないことを確認できます。

len(set("AAAAAAAA"))
于 2013-01-14T15:03:25.333 に答える
13

組み込み関数を使用してみてくださいall:

all(c == 'A' for c in s)
于 2013-01-14T15:03:00.223 に答える
6

この問題に別の解決策を追加する

>>> not "AAAAAA".translate(None,"A")
True
于 2013-01-14T15:26:33.063 に答える
5

文字列内のすべての文字が同じで、特定の文字と等しいかどうかを確認する必要がある場合は、すべての重複を削除し、最終結果が単一の文字と等しいかどうかを確認する必要があります。

>>> set("AAAAA") == set("A")
True

重複があるかどうかを確認したい場合は、長さを確認してください

>>> len(set("AAAAA")) == 1
True
于 2013-01-14T15:03:46.693 に答える
3

これまでのところ興味深い答え。ここに別のものがあります:

flag = True
for c in 'AAAAAAAfAAAA':
    if not c == 'A': 
        flag = False
        break

私が考えることができる唯一の利点は、矛盾した文字が見つかった場合に文字列全体をトラバースする必要がないことです。

于 2013-01-14T15:14:04.087 に答える
2
not len("AAAAAAAAA".replace('A', ''))
于 2013-01-14T15:15:17.117 に答える