あるリストの項目のanyが別のリストに存在するかどうかを確認したい。以下のコードで簡単にできますが、これを行うためのライブラリ関数があるかもしれません。そうでない場合、同じ結果を達成するためのよりPython的な方法があります。
In [78]: a = [1, 2, 3, 4, 5]
In [79]: b = [8, 7, 6]
In [80]: c = [8, 7, 6, 5]
In [81]: def lists_overlap(a, b):
....: for i in a:
....: if i in b:
....: return True
....: return False
....:
In [82]: lists_overlap(a, b)
Out[82]: False
In [83]: lists_overlap(a, c)
Out[83]: True
In [84]: def lists_overlap2(a, b):
....: return len(set(a).intersection(set(b))) > 0
....:
短い答え:not set(a).isdisjoint(b)
を使用してください。これは一般的に最速です。
2つのリストa
とb
がアイテムを共有しているかどうかをテストする一般的な方法は4つあります。最初のオプションは、両方をセットに変換し、その交差をチェックすることです:
_bool(set(a) & set(b))
_
setsはPythonのハッシュテーブルを使用して保存されるため、それらの検索はO(1)
( here を参照) Pythonの演算子の複雑さに関する詳細情報)。理論的には、これはリストn
およびm
のa
およびb
オブジェクトの平均O(n+m)
です。ただし、1)リストから最初にセットを作成する必要があり、無視できない時間を要することがあり、2)ハッシュ衝突がデータ間でまばらであることを想定しています。
2番目の方法は、リストで反復を実行するジェネレーター式を使用することです。
_any(i in a for i in b)
_
これにより、インプレースで検索できるため、中間変数に新しいメモリは割り当てられません。また、最初の検索で解決します。 ただし、リストのin
演算子は常にO(n)
です( here を参照)。
別の提案されたオプションは、リストの1つを反復処理し、セット内のもう1つを変換し、このセットのメンバーシップをテストするハイブリッドです。
_a = set(a); any(i in a for i in b)
_
4番目のアプローチは、(凍結)セットのisdisjoint()
メソッドを利用することです( here を参照)。例:
_not set(a).isdisjoint(b)
_
検索する要素が配列の先頭近くにある場合(たとえば、並べ替えられている場合)、sets交差メソッドでは中間変数に新しいメモリを割り当てる必要があるため、ジェネレーター式が優先されます。
_from timeit import timeit
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=list(range(1000))", number=100000)
26.077727576019242
>>> timeit('any(i in a for i in b)', setup="a=list(range(1000));b=list(range(1000))", number=100000)
0.16220548999262974
_
リストサイズの関数におけるこの例の実行時間のグラフは次のとおりです。
両方の軸が対数であることに注意してください。これは、ジェネレータ式の最良のケースを表しています。ご覧のように、isdisjoint()
メソッドはリストサイズが非常に小さい場合に適していますが、ジェネレーター式はリストサイズが大きい場合に適しています。
一方、検索はハイブリッド式とジェネレーター式の先頭から始まるため、共有要素が体系的に配列の最後にある場合(または両方のリストが値を共有しない場合)、互いに素な交差アプローチの設定はジェネレータ式とハイブリッドアプローチよりもはるかに高速です。
_>>> timeit('any(i in a for i in b)', setup="a=list(range(1000));b=[x+998 for x in range(999,0,-1)]", number=1000))
13.739536046981812
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=[x+998 for x in range(999,0,-1)]", number=1000))
0.08102107048034668
_
リストのサイズが大きくなると、ジェネレータ式がかなり遅くなることに注意してください。これは、前の図の100000回ではなく、1000回の繰り返しのみです。この設定は、要素が共有されていない場合にも近似しており、互いに素なアプローチおよび集合交差アプローチの最良のケースです。
乱数を使用した2つの分析を次に示します(セットアップをリギングして、ある手法または別の手法を優先する代わりに)。
共有の可能性が高い:要素は[1, 2*len(a)]
からランダムに取得されます。共有の可能性が低い:要素は[1, 1000*len(a)]
からランダムに取得されます。
これまで、この分析では、両方のリストが同じサイズであると想定していました。サイズの異なる2つのリストの場合、たとえばa
はずっと小さく、isdisjoint()
は常に高速です。
a
リストが小さいことを確認してください。そうでない場合、パフォーマンスが低下します。この実験では、a
リストサイズは定数_5
_に設定されました。
要約すれば:
not set(a).isdisjoint(b)
は常に最速です。any(i in a for i in b)
はリストのサイズが大きい場合に最速です。not set(a).isdisjoint(b)
との集合交差をテストします。これは、bool(set(a) & set(b))
よりも常に高速です。a = set(a); any(i in a for i in b)
は一般に他の方法よりも低速です。ほとんどの場合、isdisjoint()
メソッドの使用は、ジェネレーター式の実行に非常に長い時間がかかるため、最適なアプローチです。要素が共有されていない場合は非常に非効率的です。
_def lists_overlap3(a, b):
return bool(set(a) & set(b))
_
注:上記は、答えとしてブール値が必要であることを前提としています。必要なのがif
ステートメントで使用する式だけである場合は、if set(a) & set(b):
を使用します
def lists_overlap(a, b):
sb = set(b)
return any(el in sb for el in a)
これは漸近的に最適であり(最悪の場合O(n + m))、any
の短絡により交差アプローチよりも優れている場合があります。
例えば。:
lists_overlap([3,4,5], [1,2,3])
3 in sb
に到達するとすぐにTrueを返します
編集:別のバリエーション(デイブカービーのおかげで):
def lists_overlap(a, b):
sb = set(b)
return any(itertools.imap(sb.__contains__, a))
これは、ジェネレーターの理解ではなく、Cで実装されたimap
のイテレーターに依存しています。また、sb.__contains__
をマッピング関数として使用します。これがどれだけのパフォーマンスの違いをもたらすかはわかりません。まだ短絡します。
リスト内包表記でany
を使用することもできます:
any([item in a for item in b])
python 2.6以降では次のことができます:
return not frozenset(a).isdisjoint(frozenset(b))
任意の組み込み関数/ wジェネレーター式を使用できます。
def list_overlap(a,b):
return any(i for i in a if i in b)
JohnとLieが指摘したように、2つのリストで共有されているすべてのiについてbool(i)== Falseの場合、これは間違った結果をもたらします。そのはず:
return any(i in b for i in a)
関数型プログラミングスタイルでもう1つをスローします。
any(map(lambda x: x in a, b))
説明:
map(lambda x: x in a, b)
b
の要素がa
にあるブール値のリストを返します。次に、そのリストはany
に渡され、要素がTrue
である場合、単純にTrue
を返します。
この質問はかなり古いですが、人々がセットとリストを議論している間、誰もそれらを一緒に使用することを考えていないことに気付きました。 Soravuxの例に従って、
リストの最悪の場合:
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
100.91506409645081
>>> timeit('any(i in a for i in b)', setup="a=list(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
19.746716022491455
>>> timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
0.092626094818115234
そしてリストのベストケース:
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(10000)); b=list(range(10000))", number=100000)
154.69790101051331
>>> timeit('any(i in a for i in b)', setup="a=list(range(10000)); b=list(range(10000))", number=100000)
0.082653045654296875
>>> timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=list(range(10000))", number=100000)
0.08434605598449707
したがって、2つのリストを反復処理するよりも、リストを反復処理してセット内にあるかどうかを確認します。これは、リスト内を反復処理することでチェックするのに一定の時間がかかるのに対し、リスト内のリスト。
したがって、私の結論は、リストを反復処理し、それがセット内にあるかどうかを確認するです。
重複する要素が何であるかを気にしない場合は、結合リストとセットとして結合されたリストのlen
を単純に確認できます。重複する要素がある場合、セットは短くなります。
len(set(a+b+c))==len(a+b+c)
は、オーバーラップがない場合にTrueを返します。