teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

4

ソース修正

2021/09/04 03:23

投稿

退会済みユーザー
answer CHANGED
@@ -166,4 +166,130 @@
166
166
  ➡ [サンプル](https://replit.com/@suwmn50799/Si-357204-Shi-Shu-Dui-Ying#main.py)
167
167
 
168
168
 
169
- (※上記のコード、だいぶ無駄なことをやってそうな気がするので、いつか直せるといいかなと思うてます。)
169
+ (※上記のコード、だいぶ無駄なことをやってそうな気がするので、いつか直せるといいかなと思うてます。)
170
+
171
+
172
+ ### 追記(2021/9/4)
173
+
174
+ 上記の、領域の始点、終点が整数とは限らない場合のコードを以下の点で見直しました。(修正後のコードは、[replitにも上げています。](https://replit.com/@suwmn50799/357204teratail) 実行するとテスト結果が出力されます。)
175
+
176
+ - get_merged_spans(spans) の中で、走査対象の領域リストを、リストの先頭要素の領域と、2番目以降の残りの領域リストに分けて、先頭の領域と、残りの領域リストの各領域とを統合するときに、残りの領域リストのすべてに対して統合を試みていたのは無駄だったので、ひとつでも統合できる領域がみつかったら置き換えて、その後は統合を試みないでループを抜けるように修正
177
+
178
+ - get_merged_spans(spans)で与えられる引数のspansを、各領域の中点の昇順にソートしてから、統合のループに入るようにした。
179
+
180
+ - merge(a, b) は、統合可否のフラグも返していたが、統合した領域のみを返し、統合できない場合はNoneを返すようにした。
181
+
182
+ 上記の3点で修正コードが以下です。
183
+
184
+
185
+ ```python
186
+ #
187
+ # merge(a, b)
188
+ #
189
+ # 二つの領域 a, b を受け取り、 それらが統合できれば統合した領域を返す。
190
+ # 統合できなければ、Noneを返す。
191
+ #
192
+ def merge(a, b):
193
+
194
+ # a が b を含む場合は a を返す
195
+ if a[0] <= b[0] and b[1] <= a[1]:
196
+ return a
197
+
198
+ # b が a を含む場合は、b を返す
199
+ if b[0] <= a[0] and a[1] <= b[1]:
200
+ return b
201
+
202
+ # 上記以外の場合で、共通部分を持つ場合
203
+ if a[0] <= b[1] and b[0] <= a[1]:
204
+ c0 = min([a[0], b[0]])
205
+ c1 = max([a[1], b[1]])
206
+ return [c0, c1]
207
+
208
+ # aとbが共通部分を持たない場合
209
+ return None
210
+
211
+
212
+ #
213
+ # get_merged_spans(spans)
214
+ #
215
+ # 領域のリストを受け取り、統合できる領域を統合し尽くした領域リストを返す。
216
+ # (※引数の spans を変更しない)
217
+ #
218
+ def get_merged_spans(spans):
219
+
220
+ # 引数の領域リストを各領域の中点で昇順ソートしたリストを作成し、走査対象のリストとする。
221
+ target_spans = sorted(spans, key=lambda span: (span[0] + span[1]) / 2)
222
+
223
+ # 統合され尽くした領域リストを初期化(このリストを完成させて返す。)
224
+ merged_spans = []
225
+
226
+ # 統合され尽くした領域リストを作るループ(外側のループ)
227
+ while True:
228
+
229
+ # 先頭の領域と、2番目以降の領域リストに分割
230
+ head, *rest = target_spans
231
+
232
+ # 2番目以降の領域リストが空である場合
233
+ if len(rest) == 0:
234
+ # 先頭の領域を、統合され尽くした領域リストに追加して、外側のループを抜ける
235
+ merged_spans.append(head)
236
+ break
237
+
238
+ # 先頭の領域と、2番目以降の各領域を統合するループ(内側のループ)
239
+ for i, span in enumerate(rest):
240
+ # 先頭の領域 head と、2番目以降の領域 span を統合を試みる。
241
+ merged_span = merge(head, span)
242
+
243
+ # 統合される場合、統合された領域で置き換えて、内側のループをbreakで抜ける。
244
+ if merged_span:
245
+ rest[i] = merged_span
246
+ break
247
+ # 先頭の領域が、2番目以降の領域のどれとも統合できなかった場合(内側のループをbreakで抜けなかった場合)
248
+ else:
249
+ # 先頭の領域を、統合され尽くした領域リストに追加
250
+ merged_spans.append(head)
251
+
252
+ # 走査対象のリストを2番目以降のリストに置き換えて、外側のループのはじめに戻る
253
+ target_spans = rest
254
+
255
+ # 統合され尽くした領域リストを返す。
256
+ return merged_spans
257
+
258
+
259
+
260
+ if __name__ == '__main__':
261
+
262
+ from random import random, shuffle
263
+
264
+ # 統合すると [0, 20] になる 7 つの領域を含むリスト(端点はともに整数)
265
+ base_spans = [[0, 3], [2, 4], [1, 6], [5, 11], [7, 10], [8, 17], [16, 20],]
266
+
267
+ # テスト1: base_spansをシャッフルしたリスト
268
+ test1_spans = base_spans
269
+ shuffle(test1_spans)
270
+ result = get_merged_spans(test1_spans)
271
+ print(result)
272
+
273
+ # テスト2: base_spansの各領域の端点に0以上1未満の小数を加えてシャッフルしたリスト
274
+ test2_spans = [[a[0] + random(), a[1] + random()] for a in base_spans]
275
+ shuffle(test2_spans)
276
+ result = get_merged_spans(test2_spans)
277
+ print(result)
278
+
279
+ # テスト3: base_spansを +1000 および +2000 移動した領域リストをbase_spansに追加したリスト
280
+ test3_spans = [
281
+ *base_spans,
282
+ *[[a[0] + 1000, a[1] + 1000] for a in base_spans],
283
+ *[[a[0] + 2000, a[1] + 2000] for a in base_spans]
284
+ ]
285
+ shuffle(test3_spans)
286
+ result = get_merged_spans(test3_spans)
287
+ print(result)
288
+
289
+ # テスト4: テスト3の対象リストの各領域の端点に0以上1未満の小数を加えてシャッフルしたリスト
290
+ test4_spans = [[a[0] + random(), a[1] + random()] for a in test3_spans]
291
+ shuffle(test4_spans)
292
+ result = get_merged_spans(test4_spans)
293
+ print(result)
294
+
295
+ ```

3

ソース修正

2021/09/04 03:23

投稿

退会済みユーザー
answer CHANGED
@@ -163,4 +163,7 @@
163
163
 
164
164
  ```
165
165
 
166
- ➡ [サンプル](https://replit.com/@suwmn50799/Si-357204-Shi-Shu-Dui-Ying#main.py)
166
+ ➡ [サンプル](https://replit.com/@suwmn50799/Si-357204-Shi-Shu-Dui-Ying#main.py)
167
+
168
+
169
+ (※上記のコード、だいぶ無駄なことをやってそうな気がするので、いつか直せるといいかなと思うてます。)

2

ソース修正

2021/09/01 14:11

投稿

退会済みユーザー
answer CHANGED
@@ -78,4 +78,89 @@
78
78
  ワテの使い方が間違うてるやと思いますんで、どこおかしいか教えたってください m(_ _)m よろしゅうたのんます〜
79
79
 
80
80
 
81
- ➡ 一応 上に書いた確認のコードは [replitゆうWEBに挙げときました。](https://replit.com/@suwmn50799/Si-357204-1) (Run 押してもろたらすぐ実行できるで。)
81
+ ➡ 一応 上に書いた確認のコードは [replitゆうWEBに挙げときました。](https://replit.com/@suwmn50799/Si-357204-1) (Run 押してもろたらすぐ実行できるで。)
82
+
83
+ ---
84
+
85
+ **補足:**
86
+
87
+ 領域の始点、終点が整数とは限らない場合、どうなるんやろ?とモヤモヤしたもんで、作ってみました。参考になれば幸いですー
88
+ ```python
89
+ #
90
+ # merge(a, b): 2つの領域(閉区間) a と b を受け取り、以下のような2つの要素をもつタプルを返す関数
91
+ #   第1要素: a と b に共通部分があった場合、True 。共通部分がなかった場合は False
92
+ #   第2要素: a と b に共通部分があった場合は統合された領域。共通部分がなかった場合は b
93
+ #
94
+ def merge(a, b):
95
+ if a[0] <= b[1] and b[0] <= a[1]:
96
+ c0 = min([a[0], b[0]])
97
+ c1 = max([a[1], b[1]])
98
+ return True, [c0, c1]
99
+ elif a[0] <= b[0] and b[1] <= a[1]:
100
+ return True, a
101
+ elif b[0] <= a[0] and a[1] <= b[1]:
102
+ return True, b
103
+
104
+ return False, b
105
+
106
+
107
+ #
108
+ # get_merged_spans(spans):領域のリストを受け取り、それに含まれる領域を統合し尽くした領域のリストを返す
109
+ #
110
+ def get_merged_spans(spans):
111
+ merged_spans = []
112
+
113
+ while True:
114
+ head, *rest = spans
115
+ if len(rest) == 0:
116
+ merged_spans.append(head)
117
+ break
118
+
119
+ next_spans = [*map(lambda span: merge(head, span), rest)]
120
+ if any(flag for flag, _ in next_spans):
121
+ spans = [span for _, span in next_spans]
122
+ else:
123
+ merged_spans.append(head)
124
+ spans = rest
125
+
126
+ return sorted(merged_spans)
127
+
128
+
129
+
130
+ # 以下テスト実行
131
+ if __name__ == '__main__':
132
+ import random
133
+
134
+ # マージすると、[0, 20] になる 7個の(整数)領域のリスト
135
+ spans_1 = [
136
+ [0, 3], [1, 4], [2, 6], [5, 9], [7, 10], [8, 17], [16, 20],
137
+ ]
138
+ # spans_1 の各区間を、正方向に1000移動した区間のリスト
139
+ spans_2 = [[start + 1000, end + 1000] for start, end in spans_1]
140
+ # spans_1 の各区間を、正方向に200移動した区間のリスト
141
+ spans_3 = [[start + 2000, end + 2000] for start, end in spans_1]
142
+
143
+ # spans_1, spans_2, spans_3 を結合したリストを作る。(3×7=21個の閉区間を含む)
144
+ target_spans = [*spans_1, *spans_2, *spans_3]
145
+
146
+ # target_spansに含まれる全領域の始点と終点に、1未満で小数点以下最大3桁までのランダムな小数を加える。
147
+ target_spans = list(map(lambda span: [
148
+ span[0] + (int(random.random() * 1000) / 1000),
149
+ span[1] + (int(random.random() * 1000) / 1000),
150
+ ], target_spans))
151
+
152
+ # シャッフルする
153
+ random.shuffle(target_spans)
154
+
155
+ # マージされた領域のリストを取得する。
156
+ merged_spans = get_merged_spans(target_spans)
157
+
158
+ # マージされた領域の開始と終了のリストを作る
159
+ update_start_lis = [start for start, _ in merged_spans]
160
+ update_end_lis = [end for _, end in merged_spans]
161
+
162
+ print(update_start_lis, update_end_lis)
163
+
164
+ ```
165
+
166
+ ➡ [サンプル](https://replit.com/@suwmn50799/Si-357204-Shi-Shu-Dui-Ying#main.py)

1

ソース修正

2021/09/01 13:50

投稿

退会済みユーザー
answer CHANGED
@@ -70,10 +70,12 @@
70
70
 
71
71
  としてみました。そしたら
72
72
 
73
+ ```
73
- **[0, 2000] [20, 1020]**
74
+ [0, 2000] [20, 1020]
75
+ ```
74
76
 
75
77
  と出たんです。これ領域 [0, 20] と [2000, 1020] ゆう意味やんな? [2000, 1020] は始点が終点よりも大きいし、どないになってしもたんや、思いましたわ。
76
78
  ワテの使い方が間違うてるやと思いますんで、どこおかしいか教えたってください m(_ _)m よろしゅうたのんます〜
77
79
 
78
80
 
79
- ➡ 一応 上に書いた確認のコードは [replitゆうWEBに挙げときました。](https://replit.com/@suwmn50799/Si-357204-1)
81
+ ➡ 一応 上に書いた確認のコードは [replitゆうWEBに挙げときました。](https://replit.com/@suwmn50799/Si-357204-1) (Run 押してもろたらすぐ実行できるで。)