回答編集履歴

1

順序や重複を維持しないというパターンも追加

2017/06/11 02:34

投稿

raccy
raccy

スコア21735

test CHANGED
@@ -1,12 +1,24 @@
1
- Listからの削除は**大変重い処理**です。これはListが「配列(array)」と言われるデータ構造であるため、インデックスアクセスがO(1)になる代わりに、削除がO(n)になってしまうからです。
1
+ Listからの削除は**大変重い処理**です。これはListが「配列(array)」と言われるデータ構造であるため、インデックスアクセスがO(1)になる代わりに、削除がO(n)になってしまうからです。ましてや、Removeで添字ではなくオブジェクトを指定する場合は、検索もO(n)になるため、さらに遅い原因になります。
2
+
3
+
4
+
2
-
5
+ つまり、オリジナルコードは削除のリスト分繰り返しているためO(n × m)です。(nはmainのサイズ、mはdeleteのサイズ)
3
-
4
-
6
+
7
+
8
+
5
- 解決策は二つほどです。
9
+ 解決策は二つ(順序重複維持あり)+二つ(順序重複維持なし)ほどです。
10
+
11
+
12
+
6
-
13
+ ###順序と重複を維持する場合
14
+
15
+
16
+
7
-
17
+ 順序を維持し、また、重複している場合は重複したままにする場合です。
8
-
18
+
19
+
20
+
9
- ###連結リストを使う
21
+ ####連結リストを使う
10
22
 
11
23
 
12
24
 
@@ -18,11 +30,11 @@
18
30
 
19
31
 
20
32
 
21
- なぜなら、Removeで該当の項目を探す時点でO(n)の計算量が必要になるからです。削除が速いだけで見つけるのが速くなるわけではありません。
33
+ なぜなら、Removeで該当のオブジェクトを探す検索がO(n)の計算量が必要になるからです。削除が速いだけで見つけるのが速くなるわけではありません。結局O(n × m)になってしまいます。
22
-
23
-
24
-
34
+
35
+
36
+
25
- ###新たに配列を作る
37
+ ####新たに配列を作る
26
38
 
27
39
 
28
40
 
@@ -30,6 +42,46 @@
30
42
 
31
43
 
32
44
 
45
+ HashSetの作成はO(n)ですが、検索はO(1)です。配列作成も内部がO(1)ならO(n)になりますので、計算量はO(n + m)とかなり改善されます。
46
+
47
+
48
+
49
+ ###順序と重複を維持しない場合
50
+
51
+
52
+
53
+ 順序を維持し、また、重複している場合は重複したままにする場合です。
54
+
55
+
56
+
57
+ ####並び替え済みリストを使う
58
+
59
+
60
+
61
+ 並び替え済みリストの場合、検索がO(1)と高速になります。C#には並び替え済みのSortedListがありますのでこれが使用できます。ただし、削除はO(n)であり、オリジナルより速い程度です。結局O(n × m)になってしまいます。
62
+
63
+
64
+
65
+ 重複は維持されますが、順序はソートされるために維持できません。
66
+
67
+
68
+
69
+ ####ハッシュ化された集合を使う
70
+
71
+
72
+
73
+ 順序や重複を維持しなければいいのであれば、ハッシュ化された集合が使用できます。検索も削除もほぼO(1)になるため、極めて高速です。
74
+
75
+
76
+
77
+ ハッシュ化された集合としてHashSetが使用できます。なお、HashSetでは順序もそのまま維持されるようです(正式ドキュメントが見つけられなかったので、知っている人は教えて欲しい)。ただし、重複は維持されません。
78
+
79
+
80
+
81
+ ただし、ハッシュ化された集合自体の作成にO(n)必要になりますので、そこは注意してください。ということで、計算量はO(n + m)とかなり改善されます。。
82
+
83
+
84
+
33
85
  ###検証コード
34
86
 
35
87
 
@@ -112,11 +164,11 @@
112
164
 
113
165
  deleteStopwatch.ElapsedMilliseconds);
114
166
 
115
-
116
-
167
+
168
+
117
- Console.WriteLine();
169
+ Console.WriteLine();
118
-
119
-
170
+
171
+
120
172
 
121
173
  List<string> list;
122
174
 
@@ -150,7 +202,7 @@
150
202
 
151
203
 
152
204
 
153
- list = UseHashset(new List<String>(mainList),
205
+ list = CreateListWithHashset(new List<String>(mainList),
154
206
 
155
207
  new List<String>(deleteList));
156
208
 
@@ -158,6 +210,34 @@
158
210
 
159
211
  Console.WriteLine("最初: {0}", list[0]);
160
212
 
213
+
214
+
215
+ Console.WriteLine();
216
+
217
+
218
+
219
+ list = UseSortedList(new List<String>(mainList),
220
+
221
+ new List<String>(deleteList));
222
+
223
+ Console.WriteLine("サイズ: {0}", list.Count);
224
+
225
+ Console.WriteLine("最初: {0}", list[0]);
226
+
227
+
228
+
229
+ Console.WriteLine();
230
+
231
+
232
+
233
+ list = UseHashSet(new List<String>(mainList),
234
+
235
+ new List<String>(deleteList));
236
+
237
+ Console.WriteLine("サイズ: {0}", list.Count);
238
+
239
+ Console.WriteLine("最初: {0}", list[0]);
240
+
161
241
  }
162
242
 
163
243
 
@@ -242,13 +322,15 @@
242
322
 
243
323
  }
244
324
 
325
+
326
+
245
- static List<String> UseHashset(List<String> mainList,
327
+ static List<String> CreateListWithHashset(List<String> mainList,
246
328
 
247
329
  List<String> deleteList)
248
330
 
249
331
  {
250
332
 
251
- Console.WriteLine("start UseHashset");
333
+ Console.WriteLine("start CreateListWithHashset");
252
334
 
253
335
  var runStopwatch = new Stopwatch();
254
336
 
@@ -276,6 +358,96 @@
276
358
 
277
359
  }
278
360
 
361
+
362
+
363
+ static List<String> UseSortedList(List<String> mainList,
364
+
365
+ List<String> deleteList)
366
+
367
+ {
368
+
369
+ Console.WriteLine("start UseSortedList");
370
+
371
+ var runStopwatch = new Stopwatch();
372
+
373
+ runStopwatch.Start();
374
+
375
+
376
+
377
+ // SortedListに変換して処理をする。
378
+
379
+ var mainSortedList = new SortedList<String, String>(
380
+
381
+ mainList.ToDictionary(s => s));
382
+
383
+ foreach (string deleteStr in deleteList)
384
+
385
+ {
386
+
387
+ mainSortedList.Remove(deleteStr);
388
+
389
+ }
390
+
391
+ var newList = mainSortedList.Select(pair => pair.Value)
392
+
393
+ .ToList();
394
+
395
+
396
+
397
+ runStopwatch.Stop();
398
+
399
+ Console.WriteLine("run 完了: {0} ms",
400
+
401
+ runStopwatch.ElapsedMilliseconds);
402
+
403
+ return newList;
404
+
405
+ }
406
+
407
+
408
+
409
+ static List<String> UseHashSet(List<String> mainList,
410
+
411
+ List<String> deleteList)
412
+
413
+ {
414
+
415
+ Console.WriteLine("start UseHashSet");
416
+
417
+ var runStopwatch = new Stopwatch();
418
+
419
+ runStopwatch.Start();
420
+
421
+
422
+
423
+ // HashSetに変換して処理をする。
424
+
425
+ var mainHashSet = new HashSet<String>(mainList);
426
+
427
+ foreach (string deleteStr in deleteList)
428
+
429
+ {
430
+
431
+ mainHashSet.Remove(deleteStr);
432
+
433
+ }
434
+
435
+ var newList = mainHashSet.ToList();
436
+
437
+
438
+
439
+ runStopwatch.Stop();
440
+
441
+ Console.WriteLine("run 完了: {0} ms",
442
+
443
+ runStopwatch.ElapsedMilliseconds);
444
+
445
+ return newList;
446
+
447
+ }
448
+
449
+
450
+
279
451
  }
280
452
 
281
453
  ```
@@ -294,38 +466,62 @@
294
466
 
295
467
  60000個中5000個を削除
296
468
 
297
- main 作成完了: 330 ms
469
+ main 作成完了: 368 ms
298
-
470
+
299
- delete 作成完了: 23 ms
471
+ delete 作成完了: 26 ms
300
472
 
301
473
 
302
474
 
303
475
  start Original
304
476
 
305
- run 完了: 3780 ms
477
+ run 完了: 4572 ms
306
478
 
307
479
  サイズ: 55000
308
480
 
309
- 最初: 52512
481
+ 最初: 57539
310
482
 
311
483
 
312
484
 
313
485
  start UseLinkedList
314
486
 
315
- run 完了: 2846 ms
487
+ run 完了: 3258 ms
316
488
 
317
489
  サイズ: 55000
318
490
 
319
- 最初: 52512
491
+ 最初: 57539
320
-
321
-
322
-
492
+
493
+
494
+
323
- start UseHashset
495
+ start CreateListWithHashset
324
496
 
325
497
  run 完了: 13 ms
326
498
 
327
499
  サイズ: 55000
328
500
 
329
- 最初: 52512
501
+ 最初: 57539
502
+
503
+
504
+
505
+ start UseSortedList
506
+
507
+ run 完了: 2628 ms
508
+
509
+ サイズ: 55000
510
+
511
+ 最初: 10000
512
+
513
+
514
+
515
+ start UseHashSet
516
+
517
+ run 完了: 16 ms
518
+
519
+ サイズ: 55000
520
+
521
+ 最初: 57539
330
522
 
331
523
  ```
524
+
525
+
526
+
527
+ UseSortedListの最初の要素は異なります。UseHashSetの場合は重複が維持されないことに注意です。なお、順序づけしておきたい場合はSortedSetを使うという手段もあります。