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

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

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

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

Q&A

解決済

2回答

1404閲覧

数値の範囲を示す複数のリストのオーバラップしている数の最大値とその範囲を計算したい

yoshikazu.egawa

総合スコア3

Python 3.x

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

0グッド

0クリップ

投稿2021/12/17 04:19

ある数の範囲を示す2つの実数(left<right)からなるリスト[left_i, right_i](i=0, 1, 2・・)のリスト[[left_0, right_0],[left_1, right_1],[left_2, right_2],・・・] において、重なり合う部分の数の最大値を求めるコードを書こうとしています。
下記は、サンプルです。
a= [[-1.0, 20.0], [0.0, 14.0], [2.0, 3.0], [4.0, 9.0], [8.0, 15.0], [9.5, 15.0], [11.0, 14.0], [13.0, 14.0]]
ソートしたうえで、左から重なる部分を順次更新していくコードを書いたのですが、そうすると、上記のデータの場合、「3」という答えになり、「6」という正解にはなりません。
各範囲の組合せの全検索をして最大値を求めれば答えは出るのでしょうけれど、全検索以外の解法が思いつきません。全検索以外の方法で計算する考え方があれば教えてください。

hani_list = [[-1.0, 20.0], [0.0, 14.0], [2.0, 3.0], [4.0, 9.0], [8.0, 15.0], [9.5, 15.0], [11.0, 14.0], [13.0, 14.0]] hani_list = sorted(hani_list) cnt = 0 max_cnt = 0 flag = 0 history = [] for i, one in enumerate(hani_list): if i == 0: left, right = one[0], one[1] cnt += 1 else: if one[1] < right: left, right = one[0], one[1] cnt += 1 if max_cnt < cnt:max_cnt = cnt elif right <= one[1] and one[0] < right: left = one[0] cnt += 1 if max_cnt < cnt:max_cnt = cnt elif right < one[0]: history.append([left, right, cnt]) left, right = one[0], one[1] cnt = 1 history.append([left, right, cnt]) print('max_cnt:', max_cnt) print(history) #max_cnt: 3 #[[2.0, 3.0, 3], [8.0, 9.0, 2], [13.0, 14.0, 3]]

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

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

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

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

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

guest

回答2

0

考え方としては区間スケジューリング問題の類似問題一次元領域の重なり検出のアルゴリズムについての質問です。の回答が分かりやすいと思います。
実装コードとしてはC言語 直線状の線分の重複回数を求めるアルゴリズムが参考になります。

その範囲を計算したい

タイトル見逃していました。重なる範囲も得られるようにしました。

Python

1from collections import namedtuple 2from functools import cmp_to_key 3 4# 端点の比較関数 5def compEnds( p1, p2): 6 if p1.x < p2.x: 7 return -1 8 if p1.x > p2.x: 9 return 1 10 11 ret = 0 12 if p1.isLeft and (not p2.isLeft): 13 ret = 1 14 if (not p1.isLeft) and p2.isLeft: 15 ret = -1 16 17 ret *= -1 # 開区間(右端=左端は重ならないとみなす)ならコメントに 18 19 return ret 20 21def getMaxOverlaps( hani_list): 22 23 # 端点データのセットアップ 24 End = namedtuple('end', ('x', 'isLeft', 'srcPos')) 25 ends = [] 26 for i, hani in enumerate(hani_list): 27 ends.append( End(hani[0], True, i)) 28 ends.append( End(hani[1], False,i)) 29 30 # 端点データのソート 31 ends = sorted(ends, key=cmp_to_key(compEnds)) 32 33 # 重なりを求める 34 maxOverlaps = 0 35 currentOverlaps = 0 36 curRanges = set() 37 maxRanges = set() 38 for end in ends: 39 if end.isLeft: 40 currentOverlaps += 1 41 curRanges.add(end.srcPos) 42 if currentOverlaps > maxOverlaps: 43 maxOverlaps = currentOverlaps 44 maxRanges = curRanges.copy() 45 else: 46 currentOverlaps -= 1 47 curRanges.remove(end.srcPos) 48 49 maxRanges = [hani_list[n] for n in sorted(maxRanges)] 50 51 return maxOverlaps, maxRanges 52 53hani_list = [[-1.0, 20.0], [0.0, 14.0], [2.0, 3.0], [4.0, 9.0], [8.0, 15.0], [9.5, 15.0], [11.0, 14.0], [13.0, 14.0]] 54ret = getMaxOverlaps( hani_list) 55print(ret) # (6, [[-1.0, 20.0], [0.0, 14.0], [8.0, 15.0], [9.5, 15.0], [11.0, 14.0], [13.0, 14.0]]) 56 57hani_list = [[1, 2], [2, 3], [3, 4]] 58ret = getMaxOverlaps( hani_list) 59print(ret) # (2, [[1, 2], [2, 3]])

投稿2021/12/17 05:52

編集2021/12/17 06:07
can110

総合スコア38341

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

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

yoshikazu.egawa

2021/12/17 06:28

類似質問の紹介をありがとうございました。勉強させていただきます。
guest

0

ベストアンサー

  1. まずは、オーバーラップの最大段数を計算する
  2. オーバーラップが最大段数になる範囲を調べる

という2パスで考えると楽なのではないかと思います。

まず、下準備としてhani_listを始点と終点のイベントに分割してソートしておきます。
これを始点・終点のイベントを数直線の左から順にチェックできるようにします。

step1:
イベントを順に調べて、始点であればnest_levelを+1、終点であれば-1します。
nest_levelの最大値は、max_levelとして記録に残しておくことにします。

step2:
イベントリストをもう一度順に流して、nest_levelがmax_levelと等しくなる部分が求めたい期間の始点、
nest_levelがmax_level-1になる部分を終点として記録します。

python

1hani_list = [[-1.0, 20.0], [0.0, 14.0], [2.0, 3.0], [4.0, 9.0], 2 [8.0, 15.0], [9.5, 15.0], [11.0, 14.0], [13.0, 14.0]] 3 4# 下準備 5events = [] 6for s, e in hani_list: 7 events.append((s, +1)) 8 events.append((e, -1)) 9 10# step1: オーバーラップの最大段数を調べる 11max_level = 0 12nest_level = 0 13for _, ev in sorted(events): 14 nest_level += ev 15 max_level = max(max_level, nest_level) 16 17# step2: 最大段数になる期間を調べる 18ans = [] 19st = None 20for t, ev in sorted(events): 21 if ev == 1: 22 nest_level += 1 23 if nest_level == max_level: 24 st = t 25 else: 26 nest_level -= 1 27 if nest_level == max_level - 1: 28 ans.append([st, t]) 29 30print(max_level) 31print(ans)

投稿2021/12/17 05:24

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

yoshikazu.egawa

2021/12/17 06:26

丁寧でわかりやすい説明をありがとうございました。よく理解できました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問