回答編集履歴

4

バグ修正

2019/03/24 17:00

投稿

退会済みユーザー
test CHANGED
@@ -172,11 +172,11 @@
172
172
 
173
173
  def numlist(lst): # 数列をビットパターンのリストに変換
174
174
 
175
- return list(map(lambda x: bitpattern(x),lst))
175
+ return list(map(bitpattern,lst))
176
-
177
-
178
-
176
+
177
+
178
+
179
- def bitmask(m): # すべての数のビットの論理和
179
+ def bitmask(m): # すべての数の論理和
180
180
 
181
181
  return functools.reduce(lambda x, y: x | y, m)
182
182
 
@@ -202,27 +202,25 @@
202
202
 
203
203
  mlist = numlist(source) # ビットパターンのリスト
204
204
 
205
- ncount = numofbits5(bitmask(mlist)) # 数の種類(n個)
205
+ ncount = numofbits5(bitmask(mlist)) # 数の種類(n 個)
206
-
206
+
207
- (s,e) = (0,length-1) # 直近の結果のインデックス(最短リスト)
207
+ (s,e) = (0,length) # 直近の結果のインデックス(最短リスト)
208
208
 
209
209
  i = 0
210
210
 
211
- while i < length - ncount:
211
+ while ((e-s+1) != ncount) and (i < length - ncount + 1):
212
-
212
+
213
- mm = 0 # mm, k = 0, i
213
+ mm = 0
214
-
214
+
215
- for j in range(i, min(length, i+e-s)): # for j in range(i, min(length, i+e-s+1)): # 最短リストの長さしか調べない
215
+ for j in range(i, min(length, i+e-s)): # 最短リスト - 1 の長さしか調べない
216
-
216
+
217
- i = j if mm ^ mlist[j] == 0 else i # k = j if mm ^ mlist[j] == 0 else k # 先行する数字を無視できるか?
217
+ i = j if mm ^ mlist[j] == 0 else i # 先行する数字を無視できるか?
218
218
 
219
219
  mm |= mlist[j] # 累積ビットパターン
220
220
 
221
221
  if numofbits5(mm) == ncount: # n 個揃った
222
222
 
223
- #print("+:{0},{1} = {2}".format(i,j,j-i+1)) # print("+:{0},{1},{2} = {3}".format(i,k,j,j-k+1))
223
+ #print("+:{0},{1} = {2}".format(i,j,j-i+1))
224
-
225
- # i = k # k は不要
226
224
 
227
225
  s,e = (i,j) if (j-i) < (e-s) else (s,e)
228
226
 
@@ -232,10 +230,6 @@
232
230
 
233
231
  #print("-:{0},{1}".format(i,j))
234
232
 
235
- if (e-s+1) == ncount: # 最短なのは明らか
236
-
237
- break
238
-
239
233
  i += 1
240
234
 
241
235
  return (s,e)
@@ -266,15 +260,15 @@
266
260
 
267
261
  def trimright(ilist): # 右端の重複を排除
268
262
 
269
- e = ilist[-1] # e,r = ilist[-1],[]
263
+ e, leng = ilist[-1], len(ilist)
270
-
264
+
271
- for i in range(-1, (-1) - len(ilist), -1):
265
+ for i in range(-1, (-1) - leng, -1):
272
266
 
273
267
  if e != ilist[i]:
274
268
 
275
269
  break
276
270
 
277
- return min(len(ilist)+i+1,len(ilist)-1)
271
+ return min(leng+i+1,leng-1)
278
272
 
279
273
  ```
280
274
 
@@ -317,3 +311,19 @@
317
311
  print(findShortest("123345561234456"))
318
312
 
319
313
  ```
314
+
315
+ ### 修正点 (2019-03-25)
316
+
317
+ - numlist のラムダ式が冗長なので削除。
318
+
319
+ - 直近の結果のインデックス(最短リスト)の初期値を length-1 から length に変更。
320
+
321
+ - i の上限に +1
322
+
323
+ - while 条件を変更。一度もループしない場合があります。
324
+
325
+ - ソースコードをすべて最新に置き換え。
326
+
327
+
328
+
329
+ あれこれ考えた結果、バグが多なりました。申し訳ありません。なにかのお役に立てれば。

3

ソースコードを修正しました。

2019/03/24 17:00

投稿

退会済みユーザー
test CHANGED
@@ -210,19 +210,19 @@
210
210
 
211
211
  while i < length - ncount:
212
212
 
213
- mm, k = 0, i
213
+ mm = 0 # mm, k = 0, i
214
-
214
+
215
- for j in range(i, min(length, i+e-s+1)): # 最短リストの長さしか調べない
215
+ for j in range(i, min(length, i+e-s)): # for j in range(i, min(length, i+e-s+1)): # 最短リストの長さしか調べない
216
-
216
+
217
- k = j if mm ^ mlist[j] == 0 else k # 先行する数字を無視できるか?
217
+ i = j if mm ^ mlist[j] == 0 else i # k = j if mm ^ mlist[j] == 0 else k # 先行する数字を無視できるか?
218
218
 
219
219
  mm |= mlist[j] # 累積ビットパターン
220
220
 
221
221
  if numofbits5(mm) == ncount: # n 個揃った
222
222
 
223
- #print("+:{0},{1},{2} = {3}".format(i,k,j,j-k+1))
223
+ #print("+:{0},{1} = {2}".format(i,j,j-i+1)) # print("+:{0},{1},{2} = {3}".format(i,k,j,j-k+1))
224
-
224
+
225
- i = k
225
+ # i = k # k は不要
226
226
 
227
227
  s,e = (i,j) if (j-i) < (e-s) else (s,e)
228
228
 
@@ -266,7 +266,7 @@
266
266
 
267
267
  def trimright(ilist): # 右端の重複を排除
268
268
 
269
- e,r = ilist[-1],[]
269
+ e = ilist[-1] # e,r = ilist[-1],[]
270
270
 
271
271
  for i in range(-1, (-1) - len(ilist), -1):
272
272
 
@@ -277,3 +277,43 @@
277
277
  return min(len(ilist)+i+1,len(ilist)-1)
278
278
 
279
279
  ```
280
+
281
+
282
+
283
+ ### 修正点 (2019-03-24)
284
+
285
+ - k を削除する。i を直接変更することで、探索失敗後の探索を効率化。
286
+
287
+ - 「最短リストの長さ」しか調べないを「最短リストの長さ - 1」に変更。
288
+
289
+ - 「右端の重複を排除」の変数を修正。
290
+
291
+ - 例を追加。
292
+
293
+
294
+
295
+ ```python3
296
+
297
+ def findShortest(numstr):
298
+
299
+ return findMinList(list(map(int, numstr)))
300
+
301
+
302
+
303
+ print(findShortest("11222314113351413141121511"))
304
+
305
+ print(findShortest("12223141133514131411215111111111111"))
306
+
307
+ print(findShortest("11222314113351413141144444444444444421511"))
308
+
309
+ print(findShortest("2314113351413123141133511"))
310
+
311
+ print(findShortest("1233455654321234456"))
312
+
313
+ print(findShortest("123455551115555555555"))
314
+
315
+ print(findShortest("33123456521223456"))
316
+
317
+ print(findShortest("123345561234456"))
318
+
319
+ ```

2

ビットを数える解を追記

2019/03/23 18:33

投稿

退会済みユーザー
test CHANGED
@@ -125,3 +125,155 @@
125
125
  ### 結論 (2019-03-21)
126
126
 
127
127
  数字が未知であり、もっとも右端、もっとも左端だけの探索なら早いのですが、**中央の部分リスト**の効率的な探索方法を見つけられませんでした。再帰呼出しの数は文字列の長さに比例して増えてしまいます。未熟なアイデアでした。
128
+
129
+
130
+
131
+ ### ビットを数える解 (2019-03-22)
132
+
133
+
134
+
135
+ 敗北の証として、ビット 1 を数えることで数が揃っていることを判定するプログラムを書きました。概要は以下のとおり。
136
+
137
+
138
+
139
+ - 1-N の数字は未知である。数字は連続していなくてもよいが、1 -32 までしか許さない。
140
+
141
+ - 前処理で、数字 n を 32 ビットのパターンに置き換える。数字ごとにワードの n-1 番目のビットを 1 にする。
142
+
143
+ - 前処理で、使われるすべての数字のビットパターンの論理和と個数 K を調べる。
144
+
145
+ - 調査対象の数列のビットパターンの論理和をとり、ビットの個数が K なら数字が揃っていると判断する。
146
+
147
+ - 直近の最短リストのインデックスを持たせる。最短インデックスが更新される都度、探索の長さが短くなる。
148
+
149
+ - 先に同じ数字が出現した場合、排他的論理和を使って無視できるか判定する。(不完全ですが ... )
150
+
151
+
152
+
153
+ ビットの個数を数える方法は、以下の「バージョン5」を引用しました。
154
+
155
+ [ビットを数える・探すアルゴリズム](http://www.nminoru.jp/~nminoru/programming/bitcount.html)
156
+
157
+
158
+
159
+ ```python3
160
+
161
+ import functools
162
+
163
+ def findMinList(source):
164
+
165
+
166
+
167
+ def bitpattern(n): # n-1番目のビットをon
168
+
169
+ return 1 << (n - 1)
170
+
171
+
172
+
173
+ def numlist(lst): # 数列をビットパターンのリストに変換
174
+
175
+ return list(map(lambda x: bitpattern(x),lst))
176
+
177
+
178
+
179
+ def bitmask(m): # すべての数のビットの論理和
180
+
181
+ return functools.reduce(lambda x, y: x | y, m)
182
+
183
+
184
+
185
+ def numofbits5(bits): # 1のビットの数を数える http://www.nminoru.jp/~nminoru/programming/bitcount.html
186
+
187
+ bits = (bits & 0x55555555) + (bits >> 1 & 0x55555555)
188
+
189
+ bits = (bits & 0x33333333) + (bits >> 2 & 0x33333333)
190
+
191
+ bits = (bits & 0x0f0f0f0f) + (bits >> 4 & 0x0f0f0f0f)
192
+
193
+ bits = (bits & 0x00ff00ff) + (bits >> 8 & 0x00ff00ff)
194
+
195
+ return (bits & 0x0000ffff) + (bits >>16 & 0x0000ffff)
196
+
197
+
198
+
199
+ #print(source)
200
+
201
+ length = len(source) # リストの長さ
202
+
203
+ mlist = numlist(source) # ビットパターンのリスト
204
+
205
+ ncount = numofbits5(bitmask(mlist)) # 数の種類(n個)
206
+
207
+ (s,e) = (0,length-1) # 直近の結果のインデックス(最短リスト)
208
+
209
+ i = 0
210
+
211
+ while i < length - ncount:
212
+
213
+ mm, k = 0, i
214
+
215
+ for j in range(i, min(length, i+e-s+1)): # 最短リストの長さしか調べない
216
+
217
+ k = j if mm ^ mlist[j] == 0 else k # 先行する数字を無視できるか?
218
+
219
+ mm |= mlist[j] # 累積ビットパターン
220
+
221
+ if numofbits5(mm) == ncount: # n 個揃った
222
+
223
+ #print("+:{0},{1},{2} = {3}".format(i,k,j,j-k+1))
224
+
225
+ i = k
226
+
227
+ s,e = (i,j) if (j-i) < (e-s) else (s,e)
228
+
229
+ break
230
+
231
+ #else:
232
+
233
+ #print("-:{0},{1}".format(i,j))
234
+
235
+ if (e-s+1) == ncount: # 最短なのは明らか
236
+
237
+ break
238
+
239
+ i += 1
240
+
241
+ return (s,e)
242
+
243
+ ```
244
+
245
+ ### 探索回数を減らす
246
+
247
+ 改善案として、前処理で左端と右端の重複する数字の無視をあげておきます。
248
+
249
+
250
+
251
+ ```python3
252
+
253
+ def trimleft(ilist): # 左端の重複を排除
254
+
255
+ s = ilist[0]
256
+
257
+ for i in range(len(ilist)):
258
+
259
+ if s != ilist[i]:
260
+
261
+ break
262
+
263
+ return max(0,i-1)
264
+
265
+
266
+
267
+ def trimright(ilist): # 右端の重複を排除
268
+
269
+ e,r = ilist[-1],[]
270
+
271
+ for i in range(-1, (-1) - len(ilist), -1):
272
+
273
+ if e != ilist[i]:
274
+
275
+ break
276
+
277
+ return min(len(ilist)+i+1,len(ilist)-1)
278
+
279
+ ```

1

結論を追加

2019/03/22 13:25

投稿

退会済みユーザー
test CHANGED
@@ -112,10 +112,16 @@
112
112
 
113
113
  - できれば、インデックスの分布表を一度だけ作成し、それを複製、変形することで解を得たい。
114
114
 
115
- - 数列が短ければ効率が悪いかもしれないが、長くなれば効率がよくなるのではなかろうか。
115
+ - ~~数列が短ければ効率が悪いかもしれないが、長くなれば効率がよくなるのではなかろうか。~~
116
116
 
117
117
  - 再帰関数を参照透過にすれば、部分解を得るのに並行処理などが可能になるだろう。
118
118
 
119
119
 
120
120
 
121
121
  簡単な動作確認を行いましたが詳細まで詰めきれていません。問題点をご指摘ください。
122
+
123
+
124
+
125
+ ### 結論 (2019-03-21)
126
+
127
+ 数字が未知であり、もっとも右端、もっとも左端だけの探索なら早いのですが、**中央の部分リスト**の効率的な探索方法を見つけられませんでした。再帰呼出しの数は文字列の長さに比例して増えてしまいます。未熟なアイデアでした。