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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

4164閲覧

listの要素を偶数番目と奇数番目に分けて同じ処理を行いたい時の効率的な方法は?

gottadiveintopy

総合スコア736

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2019/07/21 17:29

編集2019/07/22 01:53

以下のように2次元の頂点の座標が入ったlistがあるのですが

[x1, y1, x2, y2, x3, y3, ...]

ここからxの最小値, yの最小値, xの最大値, yの最大値を求める方法、言い換えるなら全ての頂点を包括する最小の長方形(回転はさせない)を効率よく求める方法を教えて欲しいです。自分で思いついたコードは以下の二つなのですが

python3

1def method_1(points): 2 max_x, min_x, max_y, min_y = 0, 0, 0, 0 3 try: 4 it = iter(points) 5 while True: 6 x = next(it) 7 max_x = max(max_x, x) 8 min_x = min(min_x, x) 9 y = next(it) 10 max_y = max(max_y, y) 11 min_y = min(min_y, y) 12 except StopIteration: 13 pass 14 return (min_x, min_y, max_x, max_y) 15 16 17def method_2(points): 18 half_length = len(points) // 2 19 x_list = [None] * half_length 20 y_list = [None] * half_length 21 x_index = 0 22 y_index = 0 23 try: 24 it = iter(points) 25 while True: 26 x_list[x_index] = next(it) 27 y_list[y_index] = next(it) 28 x_index += 1 29 y_index += 1 30 except StopIteration: 31 pass 32 return (min(x_list), min(y_list), max(x_list), max(y_list))

もっと良い方法はあるでしょうか?

(頂点数は100未満で、非標準moduleを使わないという条件での効率的な方法を探しています、)

環境

  • Python: 3.7.1

追記

method_1()がそもそも正しく動いていませんでした。正しくは

python3

1def method_1(points): 2 try: 3 it = iter(points) 4 max_x = min_x = next(it) 5 max_y = min_y = next(it) 6 while True: 7 x = next(it) 8 max_x = max(max_x, x) 9 min_x = min(min_x, x) 10 y = next(it) 11 max_y = max(max_y, y) 12 min_y = min(min_y, y) 13 except StopIteration: 14 pass 15 return (min_x, min_y, max_x, max_y)

になると思います。

追記

私が書いた上二つの方法とhayataka2049さんの物とbamboo-novaさんの物を、頂点数を色々変えて実行時間を測ってみたので載せておきます。

python3

1def method_1(points): 2 try: 3 it = iter(points) 4 max_x = min_x = next(it) 5 max_y = min_y = next(it) 6 while True: 7 x = next(it) 8 max_x = max(max_x, x) 9 min_x = min(min_x, x) 10 y = next(it) 11 max_y = max(max_y, y) 12 min_y = min(min_y, y) 13 except StopIteration: 14 pass 15 return (min_x, min_y, max_x, max_y) 16 17 18def method_2(points): 19 half_length = len(points) // 2 20 x_list = [None] * half_length 21 y_list = [None] * half_length 22 x_index = 0 23 y_index = 0 24 try: 25 it = iter(points) 26 while True: 27 x_list[x_index] = next(it) 28 y_list[y_index] = next(it) 29 x_index += 1 30 y_index += 1 31 except StopIteration: 32 pass 33 return (min(x_list), min(y_list), max(x_list), max(y_list)) 34 35 36def method_hayataka(points): 37 even = points[::2] 38 odd = points[1::2] 39 return min(even), min(odd), max(even), max(odd) 40 41 42def method_bamboo(points): 43 x_list = [] 44 y_list = [] 45 it = iter(points) 46 for v in it: 47 x_list.append(v) 48 y_list.append(next(it)) 49 return (min(x_list), min(y_list), max(x_list), max(y_list)) 50 51 52import random 53import timeit 54 55for num_points in (10, 50, 100, 150, 200, 5000): 56 print(f'\n頂点数: {num_points // 2}') 57 points = [random.randrange(1000)%100 for _ in range(num_points)] 58 print('method_1():', timeit.timeit(lambda :method_1(points), number=1000)) 59 print('method_2():', timeit.timeit(lambda :method_2(points), number=1000)) 60 print('method_hayataka():', timeit.timeit(lambda :method_hayataka(points), number=1000)) 61 print('method_bamboo():', timeit.timeit(lambda :method_bamboo(points), number=1000))

text

1頂点数: 5 2method_1(): 0.027533224085345864 3method_2(): 0.025170729029923677 4method_hayataka(): 0.013926777057349682 5method_bamboo(): 0.01895964704453945 6 7頂点数: 25 8method_1(): 0.08861580200027674 9method_2(): 0.0367969439830631 10method_hayataka(): 0.00978929502889514 11method_bamboo(): 0.03307596605736762 12 13頂点数: 50 14method_1(): 0.11920186190400273 15method_2(): 0.05401027004700154 16method_hayataka(): 0.015701663098298013 17method_bamboo(): 0.0533315270440653 18 19頂点数: 75 20method_1(): 0.1530481429072097 21method_2(): 0.07044070307165384 22method_hayataka(): 0.018882130039855838 23method_bamboo(): 0.058508455054834485 24 25頂点数: 100 26method_1(): 0.22204544604755938 27method_2(): 0.08647163794375956 28method_hayataka(): 0.02639697107952088 29method_bamboo(): 0.06969467597082257 30 31頂点数: 2500 32method_1(): 4.2770471959374845 33method_2(): 1.7805748030077666 34method_hayataka(): 0.2754193090368062 35method_bamboo(): 1.3511152860010043

というわけで結果はすべての場合において

method_hayataka() > method_bamboo() > method_2() > method_1()の順に速いとなりました。

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

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

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

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

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

guest

回答2

0

ベストアンサー

一般論としてpythonでループを書くと遅いので、非効率だと思っても安直にやった方が良い結果を生んだりします。

python

1import random 2import timeit 3 4def method_1(points): 5 max_x, min_x, max_y, min_y = 0, 0, 0, 0 6 try: 7 it = iter(points) 8 while True: 9 x = next(it) 10 max_x = max(max_x, x) 11 min_x = min(min_x, x) 12 y = next(it) 13 max_y = max(max_y, y) 14 min_y = min(min_y, y) 15 except StopIteration: 16 pass 17 return (min_x, min_y, max_x, max_y) 18 19 20def method_2(points): 21 half_length = len(points) // 2 22 x_list = [None] * half_length 23 y_list = [None] * half_length 24 x_index = 0 25 y_index = 0 26 try: 27 it = iter(points) 28 while True: 29 x_list[x_index] = next(it) 30 y_list[y_index] = next(it) 31 x_index += 1 32 y_index += 1 33 except StopIteration: 34 pass 35 return (min(x_list), min(y_list), max(x_list), max(y_list)) 36 37def method_3(points): 38 even = points[::2] 39 odd = points[1::2] 40 return min(even), min(odd), max(even), max(odd) 41 42points = [random.randrange(100) for _ in range(150)] 43 44print(timeit.timeit(lambda :method_1(points), number=1000)) 45print(timeit.timeit(lambda :method_2(points), number=1000)) 46print(timeit.timeit(lambda :method_3(points), number=1000)) 47 48""" => 490.06728547700913623 500.029172458976972848 510.010517314949538559 52"""

あとmethod_1method_2は結果が一致しないので、チェックする必要がありそう。

投稿2019/07/21 18:00

編集2019/07/21 19:48
hayataka2049

総合スコア30933

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

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

gottadiveintopy

2019/07/21 19:20

回答ありがとうございます、おかげでbugに気づきました(^_^;)。 実行速度ばかり気にしていて他の事がおろそかになってしまったようです。 method_3()が読みやすさも実行速度も最高のようなので、これを使おうと思います。 (そもそもこの構文([::2])を忘れていました。)
hayataka2049

2019/07/21 19:44 編集

これが計算コスト的に効率が悪いのは事実で、本来は1回のループで済むと言えば済みます(このコードだとmin,maxを4回呼ぶのでそれだけで4倍+スライスで余計なオブジェクトを生成するのでそのオーバーヘッドが乗る)。 ただ、pythonレイヤでそれをやると組み込みの実装の方が速いので、勝ち目はありません。 最速を追求するならC拡張で書く? みたいな話になってしまいます(一応標準でできるはずだけど、私もやったことないし・・・)。
gottadiveintopy

2019/07/22 02:00

> これが計算コスト的に効率が悪いのは事実で... 言われてみるとそうですね。実は頂点数しだいではmethod_1()が最も速くなるんじゃないかと期待していたのですが、そんなことありませんでした。(計測結果を追記しました)。なのでやはりmethod_3()が良い気がします。
guest

0

イテレータにして二つずつ取り出した方が綺麗にかけると思います。よかったら参考にして見てください。

python

1inp = [2, 3, 1, 4, 5, 1] 2res1 = [] 3res2 = [] 4it = iter(inp) 5 6for i in it: 7 res1.append(i) 8 res2.append(next(it)) 9 10print(min(res1)) 11print(max(res1)) 12print(min(res2)) 13print(max(res2))

投稿2019/07/21 18:11

bamboo-nova

総合スコア1408

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

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

gottadiveintopy

2019/07/21 19:09 編集

回答ありがとうございます(^_^) こちらのコードの方が簡潔で読みやすいですね。ただlistに一つずつ加える事が、実行効率を落とさないか気になりました。私がmethod_2()のように一気にlistの領域を確保する方法に至ったのもそのためです。
gottadiveintopy

2019/07/22 01:14 編集

今 実際に時間を測ってみた所、bamboo-novaさんの方法はmethod_3()に次いで二番目に早いことが分かりました。よく調べもせずに効率がどうのこうの言ってすいません。後ですがfor文を使いながらも二つずつ要素を取り出せるのは盲点でした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問