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

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

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

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

Python

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

Q&A

解決済

2回答

1203閲覧

番兵を用いた線形探索

退会済みユーザー

退会済みユーザー

総合スコア0

Python 3.x

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

Python

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

0グッド

4クリップ

投稿2019/05/16 06:09

質問文

アルゴリズムの勉強をしているのですが、読んでいる書籍に

線形探索は「番兵」を用いた実装の工夫で定数倍の高速化が期待できます
(引用元: 渡部有隆 - プログラミングコンテスト攻略のためのアルゴリズムとデータ構造 (2015))

という記述があり実際に試してみることにしました。

1000個の異なる正の整数からなるリストの中に、無作為に選ばれたある整数が含まれるかどうか、また含まれる場合は何番目かを返す関数を2つ用意します。
1つは純粋な線形探索、もう一方は番兵付きです。
timeitを用いて100万回試したときのそれぞれの処理時間の平均を計測したのですが、予想に反して番兵アルゴリズムの方が遅いという結果になりました。

linear search: 5.852486592900002e-05
sentinel search: 7.166582232300002e-05

これがどうしてなのかが良く分かりません。
私のコードに問題があるのか、あるいはPythonではif判定を1000回行うよりもlist.append(),list.pop()を1度ずつ行う方が重いのでしょうか?

コード

Python

1from random import randint, sample 2from timeit import timeit 3 4# list of random unique integers 5lst = sample(range(1, 1001), 1000) 6 7# Linear search 8def lin_search(tgt: int, lst: list): 9 for i in range(len(lst)): 10 if lst[i] == tgt: 11 return i + 1 12 else: 13 return -1 14 15 16# Linear search with sentinel 17def lin_sentinel(tgt: int, lst: list): 18 lst.append(tgt) 19 i = 0 20 while lst[i] != tgt: 21 i += 1 22 lst.pop() 23 if i < len(lst): 24 return i + 1 25 else: 26 return -1 27 28 29loop = 1000000 30res_1 = timeit("lin_search(randint(1, 2001), lst)", globals=globals(), number=loop) 31res_2 = timeit("lin_sentinel(randint(1, 2001), lst)", globals=globals(), number=loop) 32print(f"linear search: {res_1 / loop}") 33print(f"sentinel search: {res_2 / loop}") 34 35> linear search: 5.852486592900002e-05 36> sentinel search: 7.166582232300002e-05

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

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

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

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

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

tiitoi

2019/05/16 06:20

このような小手先のテクニックは Python のようなスクリプト言語でやってもあまり意味がないと思います。
guest

回答2

0

ベストアンサー

番兵の効果を実感したいなら

python

1from random import randint, sample 2 3target_lst = sample(range(1, 1001), 1000) 4 5def lin_search(tgt: int, lst: list): 6 i = 0 7 while i < len(lst) and lst[i] != tgt: 8 i += 1 9 return i 10 11 12def lin_sentinel(tgt: int, lst: list): 13 lst.append(tgt) 14 i = 0 15 while lst[i] != tgt: 16 i += 1 17 del lst[-1] 18 return i

こう。

plain

1In : %timeit lin_search(1001, target_lst) 2184 µs ± 33.2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each) 3 4In : %timeit lin_sentinel(1001, target_lst) 573.9 µs ± 3.69 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each) 6 7In : %timeit lin_search(500, target_lst) 8125 µs ± 1.39 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each) 9 10In : %timeit lin_sentinel(500, target_lst) 1150.4 µs ± 793 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Pythonではfor i in iterable:while cond: i+=1より速いので比較になってないのです。

python

1def sum_for(n): 2 s = 0 3 for i in range(1, n + 1): 4 s += i 5 return s 6 7def sum_while(n): 8 s = 0 9 i = 1 10 while i <= n: 11 s += i 12 i += 1 13 return s

plain

1In : %timeit sum_for(10000) 2523 µs ± 4.84 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 3 4In : %timeit sum_while(10000) 51.02 ms ± 4.92 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

投稿2019/05/16 07:03

quickquip

総合スコア11038

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

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

退会済みユーザー

退会済みユーザー

2019/05/16 07:20

quiquiさん、hayataka2049さん、お二人ともありがとうございます。 まさか、for文がwhileよりも速いとは思いませんでした。
guest

0

pythonの比較と足し算は遅いので、ループカウンタをインクリメントしていくよりはrangeで生成した方が高速になります。主な敗因はそれです。

python

1import timeit 2 3def f1(): 4 for i in range(1000): 5 pass 6 7def f2(): 8 i = 0 9 while i < 1000: 10 i += 1 11 12print(timeit.timeit(f1, number=100)/100) # 1.6771509981481357e-05 13print(timeit.timeit(f2, number=100)/100) # 7.219993000035174e-05

投稿2019/05/16 06:56

編集2019/05/16 07:09
hayataka2049

総合スコア30933

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問