set()
を用いると、for文を使用する際と比較して処理を高速化することができます。
実際にどれくらい速くなるのか、また使う際の注意点に関して記載します。
環境
- Python: 3.11.2
for文とset()を比較する
以下、for文とset()
を比較するコードを書いています。
1~5000のランダムな文字列を10000個持つリストに対して、重複削除を行っています。
import time
import random
import functools
def measure_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"{func.__name__}の実行")
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
exec_time = end_time - start_time
print("処理時間: {:.8f}秒".format(exec_time))
return wrapper
@measure_time
def make_unique_list(lst):
rtn_lst = []
for i in lst:
if i in rtn_lst:
continue
rtn_lst.append(i)
print(f"配列の長さ:{len(rtn_lst)}")
return rtn_lst
@measure_time
def make_unique_list_with_set(lst):
rtn_lst = list(set(lst))
print(f"配列の長さ:{len(rtn_lst)}")
return rtn_lst
if __name__ == "__main__":
random_list = []
for k in range(10000):
x = random.randint(1, 5000)
random_list.append(x)
# 実行
make_unique_list(random_list)
make_unique_list_with_set(random_list)
make_unique_list()
ではfor文を使い、make_unique_list_with_set()
ではset()
を用いた記法を使っています。
実行結果
スクリプトの実行
set()
を使用した場合の方が速いことが分かりますね。
また、要素数が同じことの確認として、配列の長さを出力しています。
root@c9de83a29262:~/src# python3 test_set_speed.py
make_unique_listの実行
配列の長さ:4314
処理時間: 0.12477040秒
make_unique_list_with_setの実行
配列の長さ:4314
処理時間: 0.00029945秒
3回実行した結果を比較
1回比較するだけでは不十分なので、3回実行した結果をまとめました。
平均値で比較すると、今回のケースでは425倍高速化できたことが分かります。
1回目 | 2回目 | 3回目 | 平均値 | |
for文 | 0.12477040 | 0.12905717 | 0.13226104 | 0.12869620 |
set() | 0.00029945 | 0.00027966 | 0.00032830 | 0.00030247 |
注意点
setは順番がランダムになる
set()
を使用すると、並び順は元の配列の順番が保たれずランダムになります。
今回は元々乱数の入ったリストを扱っているため順番を気にしていないですが、並び順が変わることを避けたい場合もあるかと思います。
その場合は、sorted()
を使用して並び順を指定しましょう。
@measure_time
def make_unique_list_with_set(lst):
rtn_lst = list(set(lst))
sorted_lst = sorted(rtn_lst, reverse=False)
print(f"配列の長さ:{len(sorted_lst)}")
return rtn_lst
実行時間は以下のようになり、順番を維持するようなロジックを書いても速度は大きく変わりませんでした。
root@c9de83a29262:~/src# python3 test_set_speed.py
make_unique_listの実行
配列の長さ:4310
処理時間: 0.13345075秒
make_unique_list_with_setの実行
配列の長さ:4310
処理時間: 0.00030041秒
処理内容によって高速化度合が変わる
当たり前のことではありますが、処理内容によってどれだけ高速化されるのかは変わってきます。
以下では、1~5000ではなく1~1000の数字を持つリストの重複削除をするように変更しました。
※sorted()
は行わないロジックでの比較となります。
if __name__ == "__main__":
random_list = []
for k in range(10000):
x = random.randint(1, 1000)
random_list.append(x)
# 実行
make_unique_list(random_list)
make_unique_list_with_set(random_list)
実行結果は以下のようになり、5000個の数字を使った場合に比べて高速化の度合は下がりました。
root@c9de83a29262:~/src# python3 test_set_speed.py
make_unique_listの実行
配列の長さ:1000
処理時間: 0.04459691秒
make_unique_list_with_setの実行
配列の長さ:1000
処理時間: 0.00027800秒
set()
を使った場合に大きな変化があったのではなく、for文を使った場合の処理が速くなっていますね。
for文は処理内容の影響を受けやすいことが分かります。
まとめ
set()
を使用すると、for文の重複削除を高速化できる事例を紹介しました。
公式ドキュメントを見るとsetオブジェクトはhashableであり、set()
ではハッシュテーブルの仕組みを使用しているため処理が高速化されることが分かりました。
参考: https://docs.python.org/ja/3/library/stdtypes.html#set-types-set-frozenset
for文での重複削除を使用して実行時間が遅く困っている方は、是非set()
を使用した高速化を試してみてください。
コメント