質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

4回答

5502閲覧

Pythonの処理速度について

arare000

総合スコア26

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

2クリップ

投稿2016/11/16 10:04

処理速度に関して、Pythonの処理が並んでいるのを発見しました。
https://wiki.python.org/moin/TimeComplexity

多くはO(1)の処理速度ですが、Copyが常にO(n)の処理スピードを要する理由はなぜか、ご存じの方はいらっしゃるでしょうか。Get ItemやSet ItemがO(1)なので、なんとなくCopyもO(1)なのかなという印象なのですが・・。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

この表でのCopyはマニュアルに書かれているlist.copy()の事だと思われます。

list.copy()浅いコピーです。全体のコピーですので、listの要素全てを一つ一つ取り出してコピーしていく必要があります。つまり、一つの取り出し(Get Item)と一つの書き込み(Set Item)は要素の数だけ繰り返します。もし、Get ItemとSet Itemで実装したとしても、それぞれがO(1)であっても、そのO(1)の処理が要素数であるn回繰り返されるため、O(n)になります。実際の実装はもっと工夫して高速になっていると思いますが、要素数と処理時間が比例するという意味であるO(n)を越えて処理時間を短くすることはできません。

なお、他の処理は下記のようなコードの対比になっていると思われます。(Iterationだけこれでいいのか自信がありませんが)

Python

1a = [1, 2, 3, 4, 5] 2# Copy 3a_copy = a.copy() 4# Append[1] 5a.append(1) 6# Insert 7a.insert(2, -2) 8# Get Item 9a_item = a[3] 10# Set Item 11a[4] = 5 12# Delete Item 13del a[1] 14# Iteration 15it = iter(a) 16a_it = [x * 2 for x in it] 17# Get Slice 18a_slice = a[2:4] 19# Del Slice 20del a[2:4] 21# Set Slice 22a[2:4] = [-4, -5] 23# Extend 24a.extend([6, 7]) 25# Sort 26a.sort() 27# Multiply 28a_multi = a * 3 29# x in s 30a_in = [x * x for x in a] 31# min(s), max(s) 32a_min = min(a) 33a_max = max(a) 34# Get Length 35a_len = len(a) 36 37print("a", a) 38print("copy", a_copy) 39print("item", a_item) 40print("it", a_it) 41print("slice", a_slice) 42print("multi", a_multi) 43print("in", a_in) 44print("min", a_min) 45print("max", a_max) 46print("len", a_len)

なお、O(n)はO(1)よりも時間がかかるという意味ではありません。処理時間が、O(1)は要素数に関係無く常に一定だが、O(n)は要素数に比例して長くなると言う意味です。処理の内容次第ですが、要素数が十分少ない場合、O(n)の処理がO(1)の処理より速い場合もあり得ます。

投稿2016/11/16 11:20

raccy

総合スコア21751

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

arare000

2016/11/16 11:27

いつも大変詳細な回答、深く感謝申し上げます。 直感的にはDeep Copyの方が時間がかかりそうな印象があったのですが、これは、shallow copyがわざわざ参照元をいちいちO(n)の要素数分、チェックする必要があるということでしょうかね。逆にdeep copyは要素を丸ごとコピーするので、時間がO(1)かそれに近い反復回数で済む、というイメージで合っているでしょうか?
raccy

2016/11/16 12:44

Deep Copyは今回の話とは全く関係ありません。 listはCレベルではPyListObjectという構造体なのですが、その中の`PyObject **ob_item`というメンバーで`PyObject *`の配列として補完されています(配列の領域自体はmalloc等で確保します)。浅いコピーをする場合、ob_itemの値そのものだけをコピーするのではなく、ob_item配列の要素を一つ一つコピーするという処理になっており、一つ一つのコピーはO(1)ですが、要素の数(nは要素の数のことです)だけ繰り返すからO(n)なのです。なお、参照カウンタがあるため、一つの要素をコピーをするO(1)には参照カウンタの処理`Py_INCREF()`が含まれており、これもn回呼び出されます。 もし、参照カウンタがなければ、Cの`memcpy()`で一度にコピーするという実装も可能でしょう。実際、参照カウンタがないRubyのArray#dupでは`memcpy()`を使っています(メモリバリアの処理があるから、一概にPythonより有利というわけではありません)。しかし、`memcpy()`も計算量はO(n)になると思います。copy-on-writeな`memcpy()`であればそのときだけのO(1)はできそうですが、そうではなさそうです。リスト自体をcopy-on-writeな実装にすれば、O(1)でコピーも夢ではないんですが、コピーされた方は変更時にものすごく遅くなりそうな気もしますので、誰もやっていないんじゃ無いかなと思います。
teamikl

2016/11/16 14:08

オフトピになりますが、気付いた細かい点補足させて頂きます > Iterationだけこれでいいのか自信がありませんが Iterator は for in の部分に相当します。(iter()はイテレータを返すだけで O(1), for in で O(n)) 後、set の x in s が O(1) なので、x in s は bool 値を返す方の in だと思います。 >`memcpy()`も計算量はO(n)になると思います。 この視点が抜けていた。私の下のコメント array のcopyで O(1) と言ってるのも O(n) ですね。 詳しい解説感謝!
arare000

2016/11/17 05:54

大変ありがとうございます。まだまだ勉強不足ですみません。 少しずれますが、Pythonを使っていると、よく辞書型に遭遇するのですが、こちらはsetの集合体に参照用のkeyと変数が同時に割り当てられているということで、これをコピーする場合も基本的には同じ考え方(O(n)回処理)でお考えでしょうか。 http://docs.python.jp/3/tutorial/datastructures.html#dictionaries あるいは、CPythonとPythonは根本的に異なりますでしょうか。こんがらがってしまいすみません。
raccy

2016/11/18 13:12

setとdictの関係はちょっと調べないとわかりません。言語によってはdict相当のものでset相当のものを実装している場合もあるからです。改めて質問していただければと思います。 Pythonは言語名です。CPythonはPythonという言語の実装(環境)の一つである実装名です。CPython以外にもJythonやIronPython等といったPythonの実装が存在し、質問のサイトの表が他の実装全てに必ず当てはまるとは限りません。しかし、CPythonはPythonの実装で最も代表するものであるため、単にPythonと呼ぶ場合もあります。言語名ではなく、実装名(つまり、実際にインタプリンタとしてインストールする何か)としてPythonと言った場合は、CPythonの事を指します。 例文「Pythonを動かすために、Pythonを入れる」(前者のPythonは言語としてPythonであり、後者のPythonは実装としてのCPython、つまり、https://www.python.org/からダウンロードできるPythonなるものをインストールしている)
arare000

2016/11/18 23:53

すみません、細かく教えて頂き大変ありがとうございました。心より感謝申し上げます。
guest

0

Raccyさんのご回答で申し分ないのですが、一点だけ不思議なことに気づいたので補足。

リスト型には基本的なメソッドとして「Copy」というものが存在していないです。少なくともCPythonの実装ではそうです。そもそも基底となるシーケンス型にない。いっぽうで例えば辞書型には、基本メソッドとして「Copy」があります

ので、いったい何の実行時間をくらべているのかよくわからないのですが、「Slice」はあるので「list[:]」でくらべてると考えるしかないでしょうか。Raccyさんの挙げたlist.copy()は内部的にこれと等価な実装です (ていうか私は今日の今日まで、list.copy()なんて知らなんだ! スライスでやってました)。

なお、資料では計算時間の指標が「コンテナの要素数n」とされていますから、shallow copyのみを考えています。deep copyではありえません。

投稿2016/11/16 12:15

編集2016/11/16 12:32
ikedas

総合スコア4443

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raccy

2016/11/16 12:58

あの資料はCPythonのCのAPIに関する資料ではなくて、CPython実装におけるPythonという言語に関する資料なのではないでしょうか?私はそう捉えました…。 というか、list.copy()の事じゃない!というのであれば、Copyとは具体的にどんなコードを指すことになるのでしょうか…。copy.copy()かな? って、調べたらlist.copy()は別途定義じゃなくて、listなどを含めたシーケンシャル型の共通メソッドにしているのですね。わかりにくい。 http://docs.python.jp/3.5/library/stdtypes.html#mutable-sequence-types
ikedas

2016/11/16 13:30 編集

私が深読みしすぎたのかな。普通にマニュアル読んでれば気にならないことですね。 > CPython実装におけるPythonという言語に関する資料 ん? そうだとおもいますよ。拡張モジュールを書くときなんかにCopyがないということは、メソッドテーブルの該当スロットがNULLのままということですから。この点は標準オブジェクトも同じです。 ほかの操作とくらべ、Copyというのはそれほど普遍的な操作とはみなされていないのかと思います。(←この文、なn言ってるかわからないのではしょりました)
arare000

2016/11/17 06:34

要素数nが必要な理由は、単純にそのリストの丸写しn(1)ではダメなのは、各要素に番号が割り振られるため、それを構成するkeyの割り振り振りをコピー時にする必要があるから、ですかね。
guest

0

'https://wiki.python.org/moin/TimeComplexity'について、私も疑問です。
これ'copy'ではなくて'deepcopy'の時間では?
'copy'ならば浅いコピーでリストを作ってリスト要素のコピーだけだからそんなに処理時間を要さないと思うのですが。'deepcopy'であれば元をコピーするので納得しますが。

投稿2016/11/16 10:19

MasahikoHirata

総合スコア3778

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teamikl

2016/11/16 10:43

(CPythonでの実装では) list の実装は C言語での array なので、メモリ自体の確保は O(1) で済みますが、 GCの参照カウントのインクリメントが各要素にコピー時に必要だからだと思います。 list (リスト)でなく array (配列) モジュールの配列オブジェクトなら copy は O(1) です
arare000

2016/11/16 10:46

>MasahikoHirataさん、deepcopyとは何でしょうか。
MasahikoHirata

2016/11/16 10:47

teamiklさん、私もそれが引っかかって。少し納得。まあチリも積もれば処理時間ですので。参考になります。
arare000

2016/11/16 10:51

> teamiklさん コメントありがとうございます。GCとは何でしょうか。つまり、一つ一つの要素に、番号を振り分けているため、そのままコピーはできない、ということでしょうか。
MasahikoHirata

2016/11/16 10:53

’deepcopyとは何でしょうか。’。 'Copy'にはPythonに限らず’浅いコピー’と’深いコピー’とあります。’浅いコピー’では要素そのものではなくて、既に格納されている内容を参照できれば良い程度の情報だけをコピーする(時間がかからない。格納先と格納データの諸元だけコピー)するのに対し’深いコピー’では(実体)をコピーします(つまりnの処理)。この違いで処理速度(する仕事の多さ)が違います。
arare000

2016/11/16 10:59

>MasahikoHirata deep copy to shallow copyの違いを表す処理速度に関するドキュメントは具体的にどちらを参照すればいいでしょうか。
MasahikoHirata

2016/11/16 11:01

あれ?他の質問で回答しないでとおっしゃってますが?また解決している質問も放置ですよね?ググってご自分でお探しになる事を勧めます。
arare000

2016/11/16 11:02

はい、今後はどうか自分のポイント稼ぎのために回答しないでいただければと思います。
MasahikoHirata

2016/11/16 11:03

別に?ポイント集めると何か利点でも?
teamikl

2016/11/16 13:06 編集

ガベージコレクタの略です。メモリ管理 どこからも参照されなくなったオブジェクトを自動的に破棄する仕組みがあり、 PythonのGCの実装では、参照されている回数をカウントしています。 リストをコピーした場合、2つのリストから参照されることになるので リストの各要素は、この参照カウントを増やしていく必要があり これがあるために単純にまとめてメモリのコピーができないのだと思います。 一応、ソースコード確認しました。 listcopy内部ではsliceを呼んでいて、該当部分のコードは for の辺りです。 https://github.com/python/cpython/blob/1d0d35423dd0c13cd8306f3351e86873e6af5c5f/Objects/listobject.c#L431
arare000

2016/11/16 11:08

>teamiklさん 大変ありがとうございます。貴重な参考とさせて頂きます。
arare000

2016/11/16 11:37

>MasahikoHirata 喧嘩はあまり好きではないので、お互い平和に行きましょう。
raccy

2016/11/16 13:11

deep copyであればO(n)とは比べものにもならないほど計算量が増えますし、そもそもlistの要素数nだけでは計算量が決まりません。コメントを見る限り、shallow copyとdeep copyの違いについても全く理解していないようですので、評価を下げされていただきます。
guest

0

Pythonは、内部的にはオブジェクトで構成されているので、内部的に長さなどの情報は既に持っているので
処理時間が短い。(内部的にはポインタを入れ替えているだけ)
しかし、オブジェクト要素内部の文字列などのデータは移動する処理を数分繰り返す必要があるのでn回の時間がかかる。
と想像します。(詳しくはソースを読んでねってことになるんでしょうけど。)

投稿2016/11/16 10:15

nagaetty

総合スコア1106

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

arare000

2016/11/16 11:09

ありがとうございます。ちょっとソース確認してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問