web-dev-qa-db-ja.com

この時間の複雑さは実際にはO(n ^ 2)ですか?

私は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不変?文字列があり、_+_演算子を使用して別の文字列を追加します。必要なスペースを割り当て、元の文字列をコピーしてから、追加文字列をコピーしませんか?

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

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

またはO(n ^ 2)時間、はい?または、Pythonがアペンドを処理する方法を間違えていますか?

あるいは、釣りの方法を教えてくれたら、どうすれば自分でこれを見つけることができますか? Googleを公式ソースにしようとする試みは失敗しました。 https://wiki.python.org/moin/TimeComplexity が見つかりましたが、これには文字列には何もありません。

81
user5622964

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

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

75
user2357112

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

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

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

joinO(n)ですが、ループ外にあるので大丈夫です。

35
njzk2

Pythonの速度>最適なアルゴリズムと最速のツールを使用する :でこのテキストスニペットを見つけました。

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

24
cricket_007

将来の訪問者の場合:これはCTCIの質問であるため、学習への参照 rllib パッケージは、特にここでは必要ありません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'))
1
geekidharsh