((x == a and y == b) or (x == b and y == a))
Pythonで評価しようとしていますが、少し冗長に見えます。よりエレガントな方法はありますか?
((x == a and y == b) or (x == b and y == a))
は不自然に見えるかもしれないことに同意しますが、1)その意図は非常に明確で、Python以外のすべてのプログラマーにとって理解可能であり、不可解ではありません。代替。したがって、「よりエレガント」には深刻な欠点もあります。
((x == a and y == b) or (x == b and y == a))
Pythonで評価しようとしていますが、少し冗長に見えます。よりエレガントな方法はありますか?
((x == a and y == b) or (x == b and y == a))
は不自然に見えるかもしれないことに同意しますが、1)その意図は非常に明確で、Python以外のすべてのプログラマーにとって理解可能であり、不可解ではありません。代替。したがって、「よりエレガント」には深刻な欠点もあります。
回答:
要素がハッシュ可能である場合は、セットを使用できます。
{a, b} == {y, x}
{1, 1, 2} == {1, 2, 2}
。その時点で、sorted
またはが必要Counter
です。
あなたが得ることができる最高のことは、それらをタプルにパッケージ化することです:
if (a, b) == (x, y) or (a, b) == (y, x)
または、それをセットルックアップでラップするかもしれません
if (a, b) in {(x, y), (y, x)}
いくつかのコメントで言及されたばかりなので、いくつかのタイミングを実行しました。検索が失敗した場合、タプルとセットは同じように動作するように見えます。
from timeit import timeit
x = 1
y = 2
a = 3
b = 4
>>> timeit(lambda: (a, b) in {(x, y), (y, x)}, number=int(5e7))
32.8357742
>>> timeit(lambda: (a, b) in ((x, y), (y, x)), number=int(5e7))
31.6169182
検索が成功した場合、タプルは実際には高速ですが、
x = 1
y = 2
a = 1
b = 2
>>> timeit(lambda: (a, b) in {(x, y), (y, x)}, number=int(5e7))
35.6219458
>>> timeit(lambda: (a, b) in ((x, y), (y, x)), number=int(5e7))
27.753138700000008
私はメンバーシップのルックアップを行っているため、セットを使用することを選択しました。概念的には、セットはタプルよりもそのユースケースに適しています。特定のユースケースで2つの構造の有意差を測定した場合は、より速いものを使用します。ここではパフォーマンスは重要ではないと思います。
if (a, b) in ((x, y), (y, x))
ますか?
set
@Brilliandからのタプルソリューションへの回答でソリューションを好む理由はありますか?
タプルはそれを少し読みやすくします:
(x, y) == (a, b) or (x, y) == (b, a)
これは手掛かりを与えます:シーケンスx, y
がシーケンスと等しいかどうかをチェックしていますが、順序はa, b
無視しています。平等を設定するだけです!
{x, y} == {a, b}
,
リストではなくタプルを作成します。これ(x, y)
と(a, b)
同じタプル、あるx, y
とa, b
。
list
タイプの意味ではありません。確かにこれは混乱を招くため編集されました。
これらが数値の場合は、を使用できます(x+y)==(a+b) and (x*y)==(a*b)
。
これらが同等のアイテムである場合は、を使用できますmin(x,y)==min(a,b) and max(x,y)==max(a,b)
。
しかし((x == a and y == b) or (x == b and y == a))
、明確で、安全で、より一般的です。
3つ以上の変数の一般化として、使用できますitertools.permutations
。それはの代わりです
(x == a and y == b and z == c) or (x == a and y == c and z == b) or ...
我々は書ける
(x, y, z) in itertools.permutations([a, b, c])
そしてもちろん、2つの変数バージョン:
(x, y) in itertools.permutations([a, b])
O(N*N!)
ます。11変数の場合、これが完了するまでに1秒以上かかることがあります。(私はより高速なメソッドを投稿しましたが、それでもを受け取りO(N^2)
、1万個の変数で1秒を占有し始めます。したがって、これは高速または一般的に(書き込み可能ハッシュ可能性/順序付け可能)のどちらかであるようですが、両方ではありません:P)
あなたはすでに最も読みやすいソリューションを手に入れました。これを表現する方法は他にもありますが、文字数は少ないかもしれませんが、読むのは簡単ではありません。
値が実際に最善の方法を表すかどうかに応じて、話す名前のある関数でチェックをラップします。別の方法として、またはそれに加えて、専用の上位クラスオブジェクトでオブジェクトx、y、a、bをモデル化して、クラス等価チェックメソッドまたは専用のカスタム関数で比較のロジックと比較できます。
OPは2つの変数の場合のみに関係しているようですが、StackOverflowは後で同じ質問を検索する人のためのものでもあるので、ここで一般的なケースに少し詳しく取り組みます。以前の1つの回答にはすでにを使用した一般的な回答が含まれていますitertools.permutations()
が、その方法では、アイテムごとに順列O(N*N!)
があるため、比較が行わN!
れN
ます。(これがこの回答の主な動機でした)
最初に、ここで提示した方法の動機として、以前の回答のいくつかの方法が一般的なケースにどのように適用されるかを要約します。私はA
を参照し(x, y)
、B
を参照するために使用します。(a, b)
これは、任意の長さ(ただし同じ長さ)のタプルにすることができます。
set(A) == set(B)
高速ですが、値がハッシュ可能である場合にのみ機能し、タプルの1つに重複する値が含まれていないことを保証できます。(たとえば{1, 1, 2} == {1, 2, 2}
、@ Daniel Mesejoの回答の下で@ user2357112によって指摘されたように)
以前の方法は、セットではなくカウント付きのディクショナリを使用することで、重複する値を処理するように拡張できます(これには、すべての値をハッシュ可能にする必要があるという制限があるため、たとえば、変更可能な値list
は機能しません)。
def counts(items):
d = {}
for item in items:
d[item] = d.get(item, 0) + 1
return d
counts(A) == counts(B)
sorted(A) == sorted(B)
ハッシュ可能な値は必要ありませんが、少し遅く、代わりに順序可能な値が必要です。(つまり、complex
動作しません)
A in itertools.permutations(B)
ハッシュ可能または順序付け可能な値は必要ありませんが、すでに述べたように、O(N*N!)
複雑であるため、11項目しかなくても、完了するまでに1秒以上かかることがあります。
それで、一般的になる方法はありますが、それはかなり速くなりますか?なぜそうなのか、各アイテムの量が同じであることを「手動で」チェックすることによって(これは複雑であるO(N^2)
ため、これは大規模な入力にも適していません。私のマシンでは、1万個のアイテムが1秒以上かかることがありますが、 10項目のような小さい入力、これは他のものと同じくらい高速です)
def unordered_eq(A, B):
for a in A:
if A.count(a) != B.count(a):
return False
return True
最高のパフォーマンスを得るには、dict
最初に-basedメソッドを試して、sorted
ハッシュ化できない値が原因で失敗した場合は-basedメソッドにフォールバックし、count
順序付けできない値が原因で失敗した場合は最後に-basedメソッドにフォールバックすることをお勧めします。
x,y, a,b
:それらは整数/浮動小数点数/文字列、任意のオブジェクト、または何ですか?それらが組み込み型であり、両方x,y
をa,b
並べ替えた順序で保持できる場合は、2番目のブランチを回避できます。セットを作成すると、4つの要素のそれぞれx,y, a,b
がハッシュされることに注意してください。これは、オブジェクトの種類に完全に依存して、重要な場合とそうでない場合があります。