反復文字列追加の時間計算量は実際にはO(n ^ 2)ですか、それともO(n)ですか?


88

私はCTCIの問題に取り組んでいます。

第1章の3番目の問題は、次のような文字列を取ることです。

'Mr John Smith '

中間スペースを%20次のように置き換えるように求められます:

'Mr%20John%20Smith'

著者はこのソリューションをPythonで提供し、O(n)と呼んでいます。

def urlify(string, length):
    '''function replaces single spaces with %20 and removes trailing spaces'''
    counter = 0
    output = ''
    for char in string:
        counter += 1
        if counter > length:
            return output
        elif char == ' ':
            output = output + '%20'
        elif char != ' ':
            output = output + char
    return output

私の質問:

実際の文字列を左から右にスキャンするという点では、これはO(n)であると理解しています。しかし、Pythonの文字列は不変ではありませんか?文字列があり、+演算子を使用して別の文字列を追加した場合、必要なスペースを割り当て、元の文字列をコピーしてから、追加の文字列をコピーしませんか?

nそれぞれ長さが1の文字列のコレクションがある場合、次のようになります。

1 + 2 + 3 + 4 + 5 + ... + n = n(n+1)/2

またはO(n ^ 2)時間、そうですか?それとも、Pythonが追加を処理する方法を間違えていますか?

あるいは、釣り方を教えてくれるなら、どうやってこれを自分で見つけようか?私は公式の情報源をグーグルで検索しようとして失敗しました。https://wiki.python.org/moin/TimeComplexityを見つけましたが、文字列には何もありません。


17
誰かが作者に次のことを伝える必要がありますurllib.urlencode
2015年

10
@wim配列と文字列に関する練習問題になることを意図しています
user5622964 2015年

3
この本の目的は、面接の質問を教えることです。面接の質問では、面接対象者の思考プロセスを確認するために、車輪の再発明を行うように求められます。
James Wierzba 2015年

1
それは、Pythonですので、私がやって考えるrtrimreplace、より好ましいとの球場にあるであろうO(n)。文字列をコピーするのは、最も効率の悪い方法のようです。
OneCricketeer 2015年

2
@RNarコピーに一定の時間がかかることを説明できますか?
James Wierzba 2015年

回答:


83

Pythonの標準実装であるCPythonには、これを通常O(n)にする実装の詳細があり、バイトコード評価ループが呼び出すコード+または+=2つの文字列オペランドを使用して実装されます。Pythonは、左側の引数に他の参照がないことを検出するとrealloc、文字列のサイズを変更してコピーを回避しようとします。これは実装の詳細でありrealloc、文字列を頻繁に移動する必要がある場合は、とにかくパフォーマンスがO(n ^ 2)に低下するため、これは信頼できるものではありません。

奇妙な実装の詳細がなければ、2次のコピー量が関係するため、アルゴリズムはO(n ^ 2)になります。このようなコードは、C ++のような可変文字列を使用する言語でのみ意味があり、C ++でも使用する必要があります。+=


2
リンクしたコードを見ています...そのコードの大部分は、追加されている文字列へのポインタ/参照をクリーンアップ/削除しているようですよね?そして最後に_PyString_Resize(&v, new_len)、連結された文字列にメモリを割り当てるために実行し、次にmemcpy(PyString_AS_STRING(v) + v_len, PyString_AS_STRING(w), w_len);コピーを実行します。インプレースサイズ変更が失敗した場合は失敗しますPyString_Concat(&v, w);(これは、元の文字列アドレスの末尾にある連続したメモリが解放されていない場合を意味すると思います)。これはどのようにスピードアップを示していますか?
user5622964 2015年

以前のコメントでスペースが足りなくなりましたが、私の質問は、そのコードを正しく理解しているかどうか、およびそれらの部分のメモリ使用量/ランタイムを解釈する方法です。
user5622964 2015年

1
@ user5622964:おっと、奇妙な実装の詳細を覚えていませんでした。効率的なサイズ変更ポリシーはありません。それはただ呼びかけrealloc、最高のものを望んでいます。
user2357112は、2015

どのように機能しmemcpy(PyString_AS_STRING(v) + v_len, PyString_AS_STRING(w), w_len);ますか?cplusplus.com/reference/cstring/memcpyによると、定義void * memcpy ( void * destination, const void * source, size_t num );と説明があります。"Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination."この場合のnumは追加文字列のサイズであり、sourceは2番目の文字列のアドレスです。しかし、なぜ宛先(最初の文字列)+ len(最初の文字列)なのですか?ダブルメモリ?
user5622964 2015年

7
@ user5622964:それはポインタ演算です。奇妙な実装の詳細までCPythonソースコードを理解したい場合は、Cを知る必要があります。超凝縮バージョンはPyString_AS_STRING(v)最初の文字列のデータのアドレスであり、追加v_lenすると文字列の直後のアドレスが取得されますデータは終了します。
user2357112は、2015

39

著者は、たまたまここにある最適化に依存していますが、明示的に信頼できるわけではありません。strA = strB + strC通常O(n)は、関数を作成しますO(n^2)。ただし、プロセス全体が正しいことを確認するのは非常に簡単ですO(n)。配列を使用してください。

output = []
    # ... loop thing
    output.append('%20')
    # ...
    output.append(char)
# ...
return ''.join(output)

一言で言えば、append操作は償却され O(1)(ただしO(1)、配列を適切なサイズに事前に割り当てることで強力にすることができます)、ループを作成しますO(n)

そして、joinO(n)ですが、ループの外側にあるので問題ありません。


この答えは、文字列を連結する方法を示しているので良いです。
user877329 2017

ランタイムの計算のコンテキストでの正確な答え。
Intesar Haider

25

PythonSpeedでこのテキストスニペットを見つけました>最高のアルゴリズムと最速のツールを使用してください

文字列の連結は''.join(seq)O(n)プロセスである場合に最適です。対照的に、'+'or'+='演算子を使用すると、O(n^2)中間ステップごとに新しい文字列が作成される可能性があるため、プロセスが発生する可能性があります。CPython 2.4インタープリターは、この問題をいくらか軽減します。ただし、''.join(seq)ベストプラクティスのままです


3

将来の訪問者向け:これはCTCIの質問であるため、urllibの学習への参照ため、ここではパッケージのは必要ありません。特に、OPと本の、この質問は配列と文字に関するものです。

@ njzk2の疑似から着想を得たより完全なソリューションは次のとおりです。

text = 'Mr John Smith'#13 
special_str = '%20'
def URLify(text, text_len, special_str):
    url = [] 
    for i in range(text_len): # O(n)
        if text[i] == ' ': # n-s
            url.append(special_str) # append() is O(1)
        else:
            url.append(text[i]) # O(1)

    print(url)
    return ''.join(url) #O(n)


print(URLify(text, 13, '%20'))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.