Pythonで2つの範囲のどの値が重複するかを判断する最良の方法は何ですか?
例えば:
x = range(1,10)
y = range(8,20)
(The answer I am looking for would be the integers 8 and 9.)
範囲xが与えられた場合、別の範囲yを反復処理し、両方の範囲で共有されるすべての値を出力する最良の方法は何ですか?助けてくれてありがとう。
編集:
フォローアップとして、xがyと重複するかどうかを知る必要があることに気付きました。範囲のリストを反復処理し、重複する範囲でいくつかの追加の操作を行う方法を探しています。これを達成するための単純なTrue/Falseステートメントはありますか?
交差点を設定してみてください:
>>> x = range(1,10)
>>> y = range(8,20)
>>> xs = set(x)
>>> xs.intersection(y)
set([8, 9])
intersection
は引数として反復可能なものを受け入れることに注意してください(y
をsetに変換する必要はありません)操作)。 intersection
メソッドに相当する演算子があります:&
しかし、この場合、それは 両方の引数を設定する必要があります 。
ステップが常に+1(範囲のデフォルト)である場合、各リストをセットに変換したり、いずれかのリストを反復処理するよりも、次の方が効率的です。
range(max(x[0], y[0]), min(x[-1], y[-1])+1)
そのために set sを使用できますが、set(list)
はlist
からすべての重複エントリを削除することに注意してください。
>>> x = range(1,10)
>>> y = range(8,20)
>>> list(set(x) & set(y))
[8, 9]
1つのオプションは、次のようなリスト内包表記を使用することです。
x = range(1,10)
y = range(8,20)
z = [i for i in x if i in y]
print z
「xがyと重複する場合または重複しない場合」:
for a,b,c,d in ((1,10,10,14),
(1,10,9,14),
(1,10,4,14),
(1,10,4,10),
(1,10,4,9),
(1,10,4,7),
(1,10,1,7),
(1,10,-3,7),
(1,10,-3,2),
(1,10,-3,1),
(1,10,-11,-5)):
x = range(a,b)
y = range(c,d)
print 'x==',x
print 'y==',y
b = not ((x[-1]<y[0]) or (y[-1]<x[0]))
print ' x %s y' % ("does not overlap"," OVERLAPS ")[b]
print
結果
x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [10, 11, 12, 13]
x does not overlap y
x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [9, 10, 11, 12, 13]
x OVERLAPS y
x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
x OVERLAPS y
x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6, 7, 8, 9]
x OVERLAPS y
x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6, 7, 8]
x OVERLAPS y
x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6]
x OVERLAPS y
x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [1, 2, 3, 4, 5, 6]
x OVERLAPS y
x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
x OVERLAPS y
x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-3, -2, -1, 0, 1]
x OVERLAPS y
x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-3, -2, -1, 0]
x does not overlap y
x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-11, -10, -9, -8, -7, -6]
x does not overlap y
速度の比較:
from time import clock
x = range(-12,15)
y = range(-5,3)
te = clock()
for i in xrange(100000):
w = set(x).intersection(y)
print ' set(x).intersection(y)',clock()-te
te = clock()
for i in xrange(100000):
w = range(max(x[0], y[0]), min(x[-1], y[-1])+1)
print 'range(max(x[0], y[0]), min(x[-1], y[-1])+1)',clock()-te
結果
set(x).intersection(y) 0.951059981087
range(max(x[0], y[0]), min(x[-1], y[-1])+1) 0.377761978129
これらの実行時間の比率は2.5です
これは、ステップ= 1の場合の単純な範囲(99%の時間)の答えです。セットを使用して長距離を比較するときのベンチマークで示されているように、2500倍高速になる可能性があります重複があるかどうかを知ることで):
x = range(1,10)
y = range(8,20)
def range_overlapping(x, y):
if x.start == x.stop or y.start == y.stop:
return False
return ((x.start < y.stop and x.stop > y.start) or
(x.stop > y.start and y.stop > x.start))
>>> range_overlapping(x, y)
True
重複する値を見つけるには:
def overlap(x, y):
if not range_overlapping(x, y):
return set()
return set(range(max(x.start, y.start), min(x.stop, y.stop)+1))
ビジュアルヘルプ:
| | | |
| | | |
基準:
x = range(1,10)
y = range(8,20)
In [151]: %timeit set(x).intersection(y)
2.74 µs ± 11.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [152]: %timeit range_overlapping(x, y)
1.4 µs ± 2.91 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
結論:範囲が狭い場合でも、2倍高速です。
x = range(1,10000)
y = range(50000, 500000)
In [155]: %timeit set(x).intersection(y)
43.1 ms ± 158 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [156]: %timeit range_overlapping(x, y)
1.75 µs ± 88.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
結論:この場合、そのままrange_overlapping関数を使用します2500x高速(スピードアップの個人記録)
2つの実数値の有界区間のオーバーラップを探している場合、これは非常に便利です。
def overlap(start1, end1, start2, end2):
"""how much does the range (start1, end1) overlap with (start2, end2)"""
return max(max((end2-start1), 0) - max((end2-end1), 0) - max((start2-start1), 0), 0)
私はどこでもこれをオンラインで見つけることができなかったので、私はこれを思いつき、私はここに投稿しています。
上記の答えは、ほとんどが複雑すぎるようです。この1つのライナーはPython3で完全に機能し、入力および出力として範囲を取ります。また、不正な範囲も処理します。値を取得するには、Noneでない場合は結果を繰り返します。
# return overlap range for two range objects or None if no ovelap
# does not handle step!=1
def range_intersect(r1, r2):
return range(max(r1.start,r2.start), min(r1.stop,r2.stop)) or None
任意のステップで範囲の重複を見つけたい場合は、私のパッケージ https://github.com/avnr/rangeplus を使用できます。これはPython range()、および交差点を含むいくつかの利点:
>>> from rangeplus import Range
>>> Range(1, 100, 3) & Range(2, 100, 4)
Range(10, 100, 12)
>>> Range(200, -200, -7) & range(5, 80, 2) # can intersect with Python range() too
Range(67, 4, -14)
Range()はバインドを解除することもできます(stopがNoneの場合、範囲は+/- infinityになります):
>>> Range(1, None, 3) & Range(3, None, 4)
Range(7, None, 12)
>>> Range(253, None, -3) & Range(208, 310, 5)
Range(253, 207, -15)
交点は反復ではなく計算されるため、実装の効率はRange()の長さに依存しません。
1
のステップで範囲のみを操作していると仮定すると、数学を使用してすばやく実行できます。
def range_intersect(range_x,range_y):
if len(range_x) == 0 or len(range_y) == 0:
return []
# find the endpoints
x = (range_x[0], range_x[-1]) # from the first element to the last, inclusive
y = (range_y[0], range_y[-1])
# ensure min is before max
# this can be excluded if the ranges must always be increasing
x = Tuple(sorted(x))
y = Tuple(sorted(y))
# the range of the intersection is guaranteed to be from the maximum of the min values to the minimum of the max values, inclusive
z = (max(x[0],y[0]),min(x[1],y[1]))
if z[0] < z[1]:
return range(z[0], z[1] + 1) # to make this an inclusive range
else:
return [] # no intersection
それぞれ10 ^ 7を超える要素を持つ範囲のペアでは、重複する要素の数に関係なく、これには1秒未満がかかりました。 10 ^ 8程度の要素で試しましたが、しばらくの間コンピューターがフリーズしました。そんなに長くリストを操作しているとは思えません。