-
Notifications
You must be signed in to change notification settings - Fork 1
/
AnalysisOfAlgorithms.java
executable file
·1263 lines (1030 loc) · 39.8 KB
/
AnalysisOfAlgorithms.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package tk.dcmmc.fundamentals.Exercises;
import java.util.*;
import java.io.*;
import java.util.regex.Pattern;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdRandom;
/**
* 本文件包括Exercise 1.4中部分习题的解答
* Created by DCMMC on 2017/7/27
* Finished on 2017/7/30
* @author DCMMC
* @since 1.5
*/
class AnalysisOfAlgorithms {
/**************************************
* Methods *
**************************************/
/**
* Ex 1.4.9
* 暴力算法ThreeSum
* 时间增长倍数: O(N^3)
* @param n
* 要处理的元素个数
* @return
* 花的时间
*/
private static double threeSum(int n) {
int[] a = generateIntArray(n);
long start = System.currentTimeMillis();
int count = 0;
//时间增长倍数: O(N^3)
for (int i = 0; i < a.length; i++)
for (int j = i + 1; j < a.length; j++)
for (int k = j + 1; k < a.length; k++)
if (a[i] + a[j] + a[k] == 0)
count++;
return (System.currentTimeMillis() - start) / 1000.0;
}
/**
* Ex 1.4.9
* 时间倍率计算
* @param ratios
* 倍率比例, 即测试数据规模与上一次测试数据规模的比例, 两次测试的时间消耗之比(T(N)/T(N-1)=ratios^b)就可
* 求出幂次法则的数学模型中b的值, 一般ratios设为2
*/
private static void doublingRatio(int ratios) {
o("倍率比例: " + ratios);
o("数据规模 时间(s) 倍率(" + ratios + "^b)");
double previous = 0.0d;
//为了控制时间, 把原来的8000改成了500
for (int i = 250; i <= 500; i *= ratios) {
double time = threeSum(i);
if (previous == 0.0d)
of("%6d : %7.3f\n", i, time);
else
of("%6d : %7.3f %8d\n", i, time, (int)(time/previous));
previous = time;
}
}
/**
* Ex 1.4.15
* twoSumFaster
* O(N)
* @param n 测试数组或者给定数据规模的数量也行
* @return 匹配对数
*/
private static int twoSumFaster(int... n) {
int[] a;
if (n.length == 1) {
if (n[0] > 8000)
return 0;
else {
a = new int[n[0]];
//从文件读取ints到ints数组
try {
File intsFile = new File(AnalysisOfAlgorithms.class.getResource("8Kints.txt").toURI());
if (intsFile.exists() && intsFile.canRead()) {
Scanner sc = new Scanner(new BufferedInputStream(new FileInputStream(intsFile)), "UTF-8");
//以回车符, 空白符分隔
sc.useDelimiter(Pattern.compile("[\\s\r\n]+"));
int index = 0;
while ( sc.hasNext() && index < n[0] ) {
a[index++] = Integer.parseInt(sc.next());
}
} else {
//Debug...
o("File not found....");
}
} catch(IOException ioe) {
//IOException....
o("IOException");
throw new RuntimeException(ioe);
} catch (Exception e) {
//Exception
throw new RuntimeException(e);
}
}
} else {
a = n;
}
//Debug... 测试效率(计算数组访问次数)
int debug = 0;
//时间消耗: O(NlogN)
Arrays.sort(a);
//上一个匹配成功的i
int lastI = -1;
//上一匹配成功的i匹配到的数字个数
int lastCnt = 0;
//累积匹配到的整数对的数量
int cnt = 0;
//相对于a[a.length - 1]的偏移量
int offset = 0;
//左边从开头开始, 遍历a中的所有负数, 试图在所有正数中找到匹配的
int i;
//最差情况: 全是负数:时间消耗N
for (i = 0; i < a.length && a[i] < 0; i++) {
debug++;
//跳过比|a[i]|大的
while(a[a.length - 1 - offset] > -a[i]) {
offset++;
}
if (-a[i] == a[a.length - 1 - offset]) {
cnt++;
lastI = i;
//不管前面有没有记录过别的数对的记录, 都还原成1
lastCnt = 1;
//看看a[i]能不能在正数区匹配到更多的数
while(a[a.length - 1 - (++offset)] == -a[i]) {
cnt++;
lastCnt++;
debug++;
}
} else if (lastI > -1 && a[i] == a[lastI]) {
//-a[i] > a[a.length - 1 - offset]
//如果a[i+1]还是和a[i]一样的值, 而且原来a[i]匹配成功过, 就直接给这一个也算上之前匹配的对数
cnt += lastCnt;
}
}
//记数0的个数
//最差情况: 全是0: 时间消耗:N
int cntZero = 0;
while (i < a.length)
if (a[i++] == 0) {
debug++;
cntZero++;
}
//cntZero取2的组合
cnt += cntZero * (cntZero - 1) / 2;
//debug...
o("数组访问次数: " + debug);
return cnt;
}
/**
* Ex 1.4.15
* threeSumFaster
* O(N^2)
* @param n 测试数据规模, 不超过8000
* @return 匹配对数
*/
private static int threeSumFaster(int n) {
if (n > 8000)
return 0;
int[] a = generateIntArray(n);
//排序 O(NlogN)
Arrays.sort(a);
//Debug... 测试效率(计算数组访问次数)
int debug = 0;
//上一个匹配成功的i
int lastI = -1;
//上一次匹配成功的j
int lastJ = -1;
//上一匹配成功的i, j匹配到的数字个数
int lastCnt = 0;
//累积匹配到的整数对的数量
int cnt = 0;
//相对于a[a.length]的偏移量
int offset = 0;
//左边从开头开始, 遍历a中的所有负数, 试图在所有正数中找到匹配的
int i;
int j;
//遍历两个负数的情况, 去正数区寻找匹配的数
for (i = 0; i < a.length && a[i] < 0; i++, offset = 0)
for (j = i + 1; j < a.length && a[j] < 0; j++) {
debug++;
//跳过比|a[i]+a[j]|大的
while(a[a.length - 1 - offset] > -a[i]-a[j]) {
debug++;
offset++;
}
if (-a[i]-a[j] == a[a.length - 1 - offset]) {
cnt++;
lastI = i;
lastJ = j;
//不管前面有没有记录过别的数对的记录, 都还原成1
lastCnt = 1;
//看看a[i]和a[j]能不能在正数区匹配到更多的数
while(a[a.length - 1 - (++offset)] == -a[i]-a[j]) {
cnt++;
lastCnt++;
debug++;
}
} else if (lastI > -1 && lastJ > -1 && a[i] == a[lastI] && a[j] == a[lastJ]) {
//-a[i] > a[a.length - 1 - offset]
//如果a[i+1]还是和a[i]一样的值, 而且原来a[i]匹配成功过, 就直接给这一个也算上之前匹配的对数
cnt += lastCnt;
}
}
//记数0的个数
//最差情况: 全是0: 时间消耗:1/2N^2
int cntZero = 0;
while (i < a.length && a[i] <= 0)
if (a[i++] == 0) {
debug++;
cntZero++;
}
//cntZero取3的组合
cnt += cntZero * (cntZero - 1) * (cntZero - 2) / 6;
//遍历两个正数的情况, 去负数区寻找匹配的数
//清零偏移量, 现在的偏移量是相对于a[0]的
offset = 0;
//重置lastI和lastJ
lastI = -1;
lastJ = -1;
for (i = a.length - 1; i >= 0 && a[i] > 0; i--, offset = 0)
for (j = i - 1; j >= 0 && a[j] > 0; j--) {
debug++;
//跳过比-|a[i]+a[j]|小的
while(a[offset] < -a[i]-a[j]) {
debug++;
offset++;
}
if (-a[i]-a[j] == a[offset]) {
cnt++;
lastI = i;
lastJ = j;
lastCnt++;
//看看a[i]和a[j]能不能在负数区匹配到更多的数
while(a[++offset] == -a[i]-a[j]) {
cnt++;
lastCnt = 1;
debug++;
}
} else if (lastI > -1 && lastJ > -1 && a[i] == a[lastI] && a[j] == a[lastJ]) {
//-a[i]-a[j] > a[a.length - 1 - offset]
//如果a[i+1]还是和a[i]一样的值, 而且原来a[i]匹配成功过(同理a[j]), 就直接给这一对i,j也算上之前匹配的对数
cnt += lastCnt;
}
}
//debug...
o("数组访问次数: " + debug);
return cnt;
}
/**
* Ex 1.4.17
* Farthest Pair
* O(N)
* @param n
* 数组的元素个数
*/
private static void farthestPair(int n) {
//创建并打乱数组
double[] a = new double[n];
for (int i = 0; i < n; i++)
a[i] = i;
StdRandom.shuffle(a);
double min = a[0];
double max = a[0];
for (int i = 0; i < n; i++) {
//找出最小的数
min = Math.min(a[i], min);
max = Math.max(a[i], max);
}
o("相差最大的两个数分别是: " + min + ", "+ max);
}
/**
* Exercise 1.4.20 1.4.41
* 非递归实现, 在数组中由参数指定的范围查找key的index.
* O(logN)
* @param key 要找的那个数值
* @param a 已经由小到大排序好的int数组
* @param lo 查找范围的下限index
* @param hi 查找范围的上线offset
* @return 找不到就返回-1, 找得到就返回key在a中的index
*/
public static int rank(int key, int[] a, int lo, int hi) {
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (key < a[mid])
hi = mid - 1;
else if (key > a[mid])
lo = mid + 1;
else
return mid;
}
return -1;
}
/**
* Exercise 1.4.20 1.4.41
* 在数组中由参数指定的范围查找key的index.
* O(logN)
* @param key 要找的那个数值
* @param a 已经由大到小排序好的int数组
* @param lo 查找范围的下限index
* @param hi 查找范围的上线offset
* @return 找不到就返回-1, 找得到就返回key在a中的index
*/
public static int reverseRank(int key, int[] a, int lo, int hi) {
if (lo > hi)
return -1;
int mid = lo + (hi - lo) / 2;
if (key < a[mid])
return rank(key, a, mid + 1, hi);
else if (key > a[mid])
return rank(key, a, lo, mid - 1);
else
return mid;
}
/**
* Ex 1.4.20
* Find index of Largest
* O(logN)
* @param bitonicArray
* 双调数组
* @param low 要寻找的范围的下界
* @param high 要寻找的范围的上界
* @return 双调数组中最大的数的index
*/
private static int indexOfLargest(int[] bitonicArray, int low, int high) {
//处理几种极端情况
if (bitonicArray.length == 1)
return bitonicArray[0];
else if (bitonicArray[0] > bitonicArray[1])
return bitonicArray[0];
else if (bitonicArray[bitonicArray.length - 1] > bitonicArray[bitonicArray.length - 2])
return bitonicArray[bitonicArray.length - 1];
if (low > high)
return -1;
int mid = low + (high - low) / 2;
if (mid - 1 >= 0 && mid + 1 < bitonicArray.length && bitonicArray[mid] > bitonicArray[mid - 1]
&& bitonicArray[mid] < bitonicArray[mid + 1])
{
return indexOfLargest(bitonicArray, mid + 1, high);
} else if (mid - 1 >= 0 && mid + 1 < bitonicArray.length && bitonicArray[mid] < bitonicArray[mid - 1]
&& bitonicArray[mid] > bitonicArray[mid + 1])
{
return indexOfLargest(bitonicArray, low, mid - 1);
} else {
return mid;
}
}
/**
* Ex 1.4.20
* Bitonic Search
* use ~ 3logN compares in worst case
* Omega(2logN) compares in worst case
* @param bitonicArray
* 双调数组(就是元素大小先递增后递减的数组), 程序不会检查数组是否符合要求
* @param key 要在bitonicArray中查找的值
* @return 如果key在bitonicArray中, 就返回true
*/
private static boolean bitonicSearch(int[] bitonicArray, int key) {
int indexOfLargest = indexOfLargest(bitonicArray, 0, bitonicArray.length - 1);
if (rank(key, bitonicArray, 0, indexOfLargest) > -1
|| reverseRank(key, bitonicArray, indexOfLargest, bitonicArray.length - 1) > -1)
return true;
else
return false;
}
/**
* Ex 1.4.22
* 仅使用加减法实现的二分查找(Mihai Patrascu法)
* 即使用 斐波那契数来代替2的幂, 毕竟斐波那契数也很爆炸.
* O(log N)
* @param key 要找的数
* @param a
* 要处理的数组
* @return key在a中的index
*/
private static int fibonacciSearch(int key, int[] a) {
Arrays.sort(a);
//构造出三个相邻的斐波那契数 f1 < f2 < f3, 且f3 >= a.length
int f1 = 0;
int f2 = 1;
int f3 = f1 + f2;
while (f3 < a.length) {
f1 = f2;
f2 = f3;
f3 = f1 + f2;
}
/*
* 先设置offset(也就是相对于a[0]的偏移量)为0, 也就是在[0, f3]范围内搜索, 匹配a[offset+f1]比key相比较,
* 1) 如果小于key, 就将范围缩小到[offset + 0, offset + f1](也就是把f3向后递减两位, 使f3=f1, f2, f1也重新确定,
* 保持原来的顺序, i.e., 三个相邻的斐波那契数 f1 < f2 < f3), 然后再匹配a[offset+f1](其中的f1为新的f1).
* 2) 如果大于key, 就将范围缩小到[offset + f1, offset + f1 + f2](也就是新的偏移量为offset + f1, 加大了偏移量),
* 同时为了保持搜索范围的上限和上次一样(本来这个上限都已经超过a的有边界了), 所需需要把f3向后递推一位, 也就是f3的
* 值变为f2, 新的f2, f1也重新确定, 保持原来的顺序, i.e., 三个相邻的斐波那契数 f1 < f2 < f3.
*
* 这样递推直至f3向后递推到正好等与2, 此时要么搜索范围变成[f3 - 1, f3](这里的f3为最开始那个大于等于a.length的初始值),
* f3 - 1就是此时的offset, 然后a[offset + f1] = a[f3], 这是唯一(如果能)能够匹配的最后一个值; 或者搜索范围变成
* [0, 2](也就是[0, f3], offset还是0), 此时会比较a[f1]与key.
* 不过这里需要注意offset+1是否超过了a.length - 1这一下标, 因为有可能最后一次(或者倒数几次)判断的时候, 因为最初的f3
* 肯定是要超过数组下标的(f3 >= a.length), 所以需要判断一下(用Math.min())
*
* 最后我发现两种极端情况要么是比较a[1]要么是比较a[f3], 不如更改最初的offset为-1, 使index都往后移动一位.
*/
int offset = -1;
while (f3 > 1) {
//防止a[i]下标上溢.
int i = Math.min(offset + f1, a.length - 1);
if (a[i] > key) {
//更改范围为[offset + 0, offset + f1], 也就是三个斐波那契数都往后移动两位.
f2 = f2 - f1;
f1 = f1 - f2;
f3 = f1 + f2;
} else if (a[i] < key) {
//更改范围为[offset + f1, offset + f1 + f2], 也就是扩大offset(范围下限), 保持范围上限不变
offset += f1;
f1 = f2 - f1;
f2 = f3 - f2;
f3 = f1 + f2;
} else {
//匹配成功
return i;
}
}
//匹配失败
return -1;
}
/**
* Ex 1.4.23
* 有点难, Google了才找到时间 ~ logN, 时间最差为O(N), 空间O(1)的解法
* Using Farey Sequence:
* In mathematics, the Farey sequence of order n is the sequence of completely reduced
* fractions between 0 and 1 which when in lowest terms have denominators less than or
* equal to n, arranged in order of increasing size.
* if p/q has neighbours a/b and c/d in some Farey sequence, with a/b < p/q < c/d, then
* p/q = (a + b)/(c + d)
* @param N 0 < p < q < N
* @param x 要找到与x最接近或等于x的有理数, x必须是[0, 1]
*/
private static void fareyFractionSearch(int N, double x) {
//最终的答案
int p = 0, q = 0;
//A, B为搜索的范围, num表示分子, denom表示分母
int numA = 0, denomA = 1;
int numB = 1, denomB = 1;
//A, B中间的Farey数
double mid;
while (denomA < N && denomB < N) {
//令mid最A和B中间的Farey数
mid = (1.0 * numA + numB) / (denomA + denomB);
//这里用到了一个公理(易证): 分母都小于N的两分数的差不小于1/(n^2)
//由此可得只要|x - mid|的差小于等于1/(n^2), 那么x和mid之间一定不含有一个满足分子分母都小于N的分数.
//然而, 书上给的这个hints就是坑啊, 这只是一个必要条件而已, 就是有可能: 两个分子分母都小于N的相距最近
//的分数, 可能他们的差为2/(N^2), 然而x与较小的那个分数的差却有1.5/(N^2), 所以下面的if句并不能判断是
//x的floor的分数还是x的ceil的分数... 甚至都可能找不到...
if (Math.abs(x - mid) <= 1.0 / ((1.0 * N) * N) ) {
if (denomA + denomB < N) {
//如果这个答案的分子分母小于N就直接得出答案
p = numA + numB;
q = denomA + denomB;
break;
} else if (denomA > denomB) {
//找上一次递推的farey数
//A就是上一轮递推的farey数
p = numA;
q = denomA;
break;
} else {
p = numB;
q = denomB;
break;
}
} else if (x < mid) {
numB = numA + numB;
denomB = denomA + denomB;
} else if (x > mid){
//x > mid
numA = numA + numB;
denomA = denomA + denomB;
}
}
if (x == 0.0d) {
p = 0;
q = 1;
} else if (x == 1.0d) {
p = 1;
q = 1;
}
//o("分子分母都小于" + N + "的有理数中最接近" + x + "并且小于" + x + "的有理数是: " + p + "/" + q);
//似乎跟题目的要求还是有点相悖...
o("分子分母都小于" + N + "的有理数中最接近" + x + "的有理数是: " + p + "/" + q);
}
/**
* Ex 1.4.24
* EggsDrop问题
* ~logN方法
* @param N
* 楼层高度
* @param F
* 鸡蛋恰好摔碎的楼层
* @return 程序猜到的楼层数
*/
private static int eggsDrop(int N, int F) {
//debug
int debugCnt = 0;
int debugEggs = 0;
//默认在一楼, 这样所有的鸡蛋抛下去都会摔碎
int f = 1;
int lo = 1;
int hi = N;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (mid < F && mid + 1 < F) {
lo = mid + 1;
debugCnt += 2;
} else if (mid >= F) {
hi = mid - 1;
debugCnt += 2;
debugEggs += 1;
} else if (mid < F && mid + 1 >= F) {
debugCnt += 2;
debugEggs++;
f = mid + 1;
break;
}
}
o("tosses: " + debugCnt + ", eggs: " + debugEggs);
return f;
}
/**
* Ex 1.4.24
* EggsDrop问题
* ~2logF投掷, ~logF鸡蛋
* @param N
* 楼层高度
* @param F
* 鸡蛋恰好摔碎的楼层
* @return 程序猜到的楼层数
*/
private static int eggsDrop2(int N, int F) {
//debug
int debugCnt = 0;
int debugEggs = 0;
//默认在一楼, 这样所有的鸡蛋抛下去都会摔碎
int f = 1;
int lastSqrt = N;
//找到那个事鸡蛋不会碎的sqrt(n)^k
while ( (int)Math.sqrt(lastSqrt) >= F ) {
lastSqrt = (int)Math.sqrt(lastSqrt);
debugCnt++;
debugEggs++;
}
//在(int)Math.sqrt(lastSqrt)到lastSqrt之间二分搜索
int lo = (int)Math.sqrt(lastSqrt);
int hi = lastSqrt;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (mid < F && mid + 1 < F) {
lo = mid + 1;
debugCnt += 2;
} else if (mid >= F) {
hi = mid - 1;
debugCnt += 1;
debugEggs += 1;
} else if (mid < F && mid + 1 >= F) {
debugCnt += 2;
debugEggs++;
f = mid + 1;
break;
}
}
o("tosses: " + debugCnt + ", eggs: " + debugEggs);
return f;
}
/**
* Ex 1.4.24
* EggsDrop问题
* ~2logF投掷, ~logF鸡蛋
* @param N
* 楼层高度
* @param F
* 鸡蛋恰好摔碎的楼层
* @return 程序猜到的楼层数
*/
private static int eggsDrop3(int N, int F) {
//debug...
int debugCnt = 0;
int debugEggs = 0;
//区块的左端点相对于一楼的offset
int leftOffset = 0;
//区块的长度
int length = 1;
//result
int f = 0;
//初始第一个区块的左右offset都是0, 因为在第一个区块只有一层楼
while (leftOffset + length <= N) {
//leftOffset + legth表示当前区块的右端点
if (leftOffset + length >= F) {
debugEggs++;
debugCnt++;
//从区块左端点开始遍历
int last = leftOffset + 1;
while (last++ < F)
debugCnt++;
debugEggs++;
f = last - 1;
break;
}
debugCnt++;
leftOffset += length;
length++;
}
o("tosses: " + debugCnt + ", eggs: " + debugEggs);
return f;
}
/**
* Ex 1.4.41
* 暴力算法TwoSum
* 时间增长倍数: O(N^2)
* @param a
* 要处理的数组
* @return
* 数组中两个元素之和为0的组数
*/
private static int countTwoSum(int[] a) {
int count = 0;
//时间增长倍数: O(N^2)
for (int i = 0; i < a.length; i++)
for (int j = i + 1; j < a.length; j++)
if (a[i] + a[j] == 0)
count++;
return count;
}
/**
* Ex 1.4.41
* 归并排序+二分搜索算法TwoSum
* 时间增长倍数: O(N*logN)
* @param a
* 要处理的数组
* @return
* 数组中两个元素之和为0的组数
*/
private static int countTwoSumFast(int[] a) {
int count = 0;
//时间增长倍数: O(logN)
//先归并排序
Arrays.sort(a);
//时间增长倍数: O(NlogN)
//如果在a[i]的后面找到了值为-a[i]的元素, count就加一
for (int i = 0; i < a.length; i++)
if (rank(-a[i], a, 0, a.length - 1) > i)
count++;
return count;
}
/**
* Ex 1.4.41
* 暴力算法ThreeSum
* 时间增长倍数: O(N^3)
* @param a
* 要处理的数组
* @return
* 数组中三个元素之和为0的组数
*/
private static int countThreeSum(int[] a) {
int count = 0;
//时间增长倍数: O(N^3)
for (int i = 0; i < a.length; i++)
for (int j = i + 1; j < a.length; j++)
for (int k = j + 1; k < a.length; k++)
if (a[i] + a[j] + a[k] == 0)
count++;
return count;
}
/**
* Ex 1.4.41
* 归并排序+二分搜索算法ThreeSum
* 时间增长倍数: O(N^3)
* @param a
* 要处理的数组
* @return
* 数组中三个元素之和为0的组数
*/
private static int countThreeSumFast(int[] a) {
int count = 0;
//时间增长倍数: O(logN)
//先归并排序
Arrays.sort(a);
//时间增长倍数: O(N^2*logN)
for (int i = 0; i < a.length; i++)
for (int j = i + 1; j < a.length; j++)
if (rank(-a[i]-a[j], a, 0, a.length - 1) > j)
count++;
return count;
}
/**
* 归并排序+二分搜索算法ThreeSum
* 时间增长倍数: O(N^3)
* @param n
* 要处理的元素个数
* @return
* 花的时间
*/
private static double threeSumFast(int n) {
int[] a = generateIntArray(n);
long start = System.currentTimeMillis();
int count = 0;
//时间增长倍数: O(logN)
//先归并排序
Arrays.sort(a);
//时间增长倍数: O(N^2*logN)
for (int i = 0; i < a.length; i++)
for (int j = i + 1; j < a.length; j++)
if (rank(-a[i]-a[j], a, 0, a.length - 1) > j)
count++;
return (System.currentTimeMillis() - start) / 1000.0;
}
/**
* Ex 1.4.41
* DoublingTest
* @throws RuntimeException
* 读取文件的时候如果出问题就抛出RuntimeException
*/
private static void doublingTest() throws RuntimeException {
String[] testFiles = {"1Kints.txt", "2Kints.txt", "4Kints.txt", "8Kints.txt"};
//做4轮测试, 为了节省时间, 还是把原来的4调成2算了
for (int i = 0; i < 2; i++) {
of( "%8dints:\n", (int)(Math.pow(2, i)*1000) );
int[] ints = new int[(int)(Math.pow(2, i)*1000)];
//从文件读取ints到ints数组
try {
File intsFile = new File(AnalysisOfAlgorithms.class.getResource(testFiles[i]).toURI());
if (intsFile.exists() && intsFile.canRead()) {
Scanner sc = new Scanner(new BufferedInputStream(new FileInputStream(intsFile)), "UTF-8");
//以回车符, 空白符分隔
sc.useDelimiter(Pattern.compile("[\\s\r\n]+"));
int index = 0;
while ( sc.hasNext() && index < (int)(Math.pow(2, i)*1000) ) {
ints[index++] = Integer.parseInt(sc.next());
}
} else {
//Debug...
o("File not found....");
}
} catch(IOException ioe) {
//IOException....
o("IOException");
throw new RuntimeException(ioe);
} catch (Exception e) {
//Exception
throw new RuntimeException(e);
}
//TwoSum
long startTime = System.currentTimeMillis();
countTwoSum(ints);
of(" TwoSum: %5.8fs\n", (System.currentTimeMillis() - startTime) / 1000.0);
//TwoSumFast
startTime = System.currentTimeMillis();
countTwoSumFast(ints);
of(" TwoSumFast: %5.8fs\n", (System.currentTimeMillis() - startTime) / 1000.0);
//ThreeSum
startTime = System.currentTimeMillis();
countThreeSum(ints);
of(" ThreeSum: %5.8fs\n", (System.currentTimeMillis() - startTime) / 1000.0);
//ThreeSumFast
startTime = System.currentTimeMillis();
countThreeSumFast(ints);
of("ThreeSumFast: %5.8fs\n", (System.currentTimeMillis() - startTime) / 1000.0);
String header = "";
//打印分隔符
for (int m = 0; m < 40; m++)
header += "-";
o(header);
}
}
/**
* 从8Kints读取指定的ints到数组
* @param n
* 要生成的数组的大小
* @throws RuntimeException 出现问题就直接抛RuntimeException
*/
private static int[] generateIntArray(int n) throws RuntimeException {
int[] a = new int[n];
if (n > 8000)
throw new RuntimeException("n不能大于8000");
//从文件读取ints到ints数组
try {
File intsFile = new File(AnalysisOfAlgorithms.class.getResource("8Kints.txt").toURI());
if (intsFile.exists() && intsFile.canRead()) {
Scanner sc = new Scanner(new BufferedInputStream(new FileInputStream(intsFile)), "UTF-8");
//以回车符, 空白符分隔
sc.useDelimiter(Pattern.compile("[\\s\r\n]+"));
int index = 0;
while ( sc.hasNext() && index < n ) {
a[index++] = Integer.parseInt(sc.next());
}
} else {
//Debug...
o("File not found....");
}
} catch(IOException ioe) {
//IOException....
o("IOException");
throw new RuntimeException(ioe);
} catch (Exception e) {
//Exception
throw new RuntimeException(e);
}
return a;
}
/**************************************
* 我的一些方法和client测试方法 *
**************************************/
/**
* 那个控制台输出的语句太长啦, 搞个方便一点的.
* @param obj 要输出的String.
*/
private static void o(Object obj) throws IllegalArgumentException {
System.out.println(obj);
}
/**
* 那个控制台输出的语句太长啦, 搞个方便一点的.
* 重载的一个版本, 不接受任何参数, 就是为了输出一个回车.
*/