【演算法】二分查詢與暴力查詢(白名單過濾)

Richaaaard發表於2015-07-07

 二分查詢與暴力查詢。

如果可能,我們的測試用例都會通過模擬實際情況來展示當前演算法的必要性。這裡該過程被稱為白名單過濾。具體來說,可以想象一家信用卡公司,它需要檢查客戶的交易賬號是否有效。為此,它需要:

  • 將客戶的賬號儲存在一個檔案中,我們稱它為白名單;
  • 從標準輸入中得到每筆交易的賬號;
  • 使用這個測試用例在標準輸出中列印所有與任何客戶無關的賬號,公司很可能拒絕此類交易。

在一家有上百萬客戶的大公司中,需要處理數百萬甚至更多的交易都是很正常的。為了模擬這種情況,我們提供了檔案largeW.txt(100萬條:約6.7M)和largeT.txt(1000萬條:約86M)。其中largeW.txt表示白名單,largeT.txt表示目標檔案

給出暴力查詢法(順序查詢)編寫一個程式BruteForceSearch,在你的計算機上比較它和BinarySearch處理largeW.txt(100萬條:約6.7M)和largeT.txt(1000萬條:約86M)所需的時間。

說明:

演算法第四版中提及的暴力查詢法實為順序查詢法,詳見程式碼:

1     public static int rank(int key, int[] a) {
2         for (int i = 0; i < a.length; i++) {
3             if (a[i] == key) return i;
4         }
5         return -1;
6     }


效能:

一個程式只是可用往往是不夠的。例如,上面rank()的實現也可以很簡單,它會檢查陣列的每個元素,甚至都不需要陣列是有序的。

有了這個簡單易懂的解決方案,我們為什麼還需要歸併排序和二分查詢呢?計算機用rank()方法的暴力實現處理大量輸入(比如含有100萬個條目的白名單和1000萬條交易)非常慢。沒有如二分查詢或者歸併排序這樣的高效演算法,解決大規模的白名單問題是不可能得。良好的效能常常是極為重要的。

二分查詢演算法程式碼:

 1    public static int rank(int key, int[] a) {
 2         int lo = 0;
 3         int hi = a.length - 1;
 4         while (lo <= hi) {
 5             // Key is in a[lo..hi] or not present.
 6             int mid = lo + (hi - lo) / 2;
 7             if      (key < a[mid]) hi = mid - 1;
 8             else if (key > a[mid]) lo = mid + 1;
 9             else return mid;
10         }
11         return -1;
12     }


實驗程式碼:

 

 1 package com.beyond.algs4.experiment;
 2 
 3 import java.io.File;
 4 import java.util.Arrays;
 5 
 6 import com.beyond.algs4.lib.BinarySearch;
 7 import com.beyond.algs4.lib.StdIn;
 8 import com.beyond.algs4.lib.StdOut;
 9 import com.beyond.algs4.std.In;
10 
11 public class PerfBruteForceSearch {
12 
13     /**
14      * @param args
15      */
16     public static void main(String[] args) {
17         String whitelist = StdIn.readString();
18         int[] whitelistArray = readlist(whitelist);        
19         String targetlist = StdIn.readString();
20         int[] targetlistArray = readlist(targetlist);
21         
22         long t1 = System.currentTimeMillis();
23 //        for (int i = 0; i < targetlistArray.length; i++) {
24 //            BruteForceSearch.rank(targetlistArray[i], whitelistArray);    
25 //        }
26 //        StdOut.println(String.format("BruteForceSearch in %d milliseconds", (long) (System.currentTimeMillis() - t1)));
27 //        
28 //        t1 = System.currentTimeMillis();
29         Arrays.sort(whitelistArray);
30         for (int i = 0; i < targetlistArray.length; i++) {
31             BinarySearch.rank(targetlistArray[i], whitelistArray);    
32         }
33         StdOut.println(String.format("BinarySearch in %d milliseconds", (long) (System.currentTimeMillis() - t1)));
34     }
35 
36     private static int[] readlist(String whitelist) {
37         File fWhitelist = new File(whitelist);
38         In in = new In(fWhitelist);
39         int[] whitelistArray = in.readAllInts();
40         return whitelistArray;
41     }
42 
43 }

實驗結果:

1)tinyW.txt 與 tinyT.txt

./TinyW.txt
./TinyT.txt
BruteForceSearch in 0 milliseconds
BinarySearch in 1 milliseconds

2)largeW.txt 與 largeT.txt(BruteForceSearch in hours)

./largeW.txt
./largeT.txt
BinarySearch in 2399 milliseconds

補充說明:

實驗方法忽略讀取測試資料檔案對演算法的影響

實驗方法忽略BruteForceSearch與BinarySearch執行的影響

注意

在記憶體不夠時,可能出現錯誤:Java heap space

 1 ./largeW.txt
 2 ./largeT.txt
 3 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
 4     at java.nio.HeapCharBuffer.<init>(Unknown Source)
 5     at java.nio.CharBuffer.allocate(Unknown Source)
 6     at java.util.Scanner.makeSpace(Unknown Source)
 7     at java.util.Scanner.readInput(Unknown Source)
 8     at java.util.Scanner.next(Unknown Source)
 9     at com.beyond.algs4.std.In.readAll(In.java:247)
10     at com.beyond.algs4.std.In.readAllStrings(In.java:322)
11     at com.beyond.algs4.std.In.readAllInts(In.java:348)
12     at com.beyond.algs4.experiment.PerfBruteForceSearch.readlist(PerfBruteForceSearch.java:39)
13     at com.beyond.algs4.experiment.PerfBruteForceSearch.main(PerfBruteForceSearch.java:20)

在更改win32_x86版本eclipse 並把eclipse.ini更新為-Xmx2048m超過1024時,eclipse會出錯“Failed to create the Java Virtual Machine”。建議更換64位版本。

計算機基本配置

處理器: Inter(R) Pentium(R) CPU G3220 @3.00GHz

記憶體:8.00GB

系統型別:64位作業系統

軟體環境

IDE: Version: Mars Release (4.5.0)

JVM:

 1 -startup
 2 plugins/org.eclipse.equinox.launcher_1.3.100.v20150511-1540.jar
 3 --launcher.library
 4 plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.300.v20150602-1417
 5 -product
 6 org.eclipse.epp.package.jee.product
 7 --launcher.defaultAction
 8 openFile
 9 --launcher.XXMaxPermSize
10 256M
11 -showsplash
12 org.eclipse.platform
13 --launcher.XXMaxPermSize
14 256m
15 --launcher.defaultAction
16 openFile
17 --launcher.appendVmargs
18 -vmargs
19 -Dosgi.requiredJavaVersion=1.7
20 -Xms512m
21 -Xmx2048m

 

參考資料:

演算法 第四版  謝路雲 譯 Algorithms Fourth Edition [美] Robert Sedgewick, Kevin Wayne著

http://algs4.cs.princeton.edu/home/

原始碼下載連結:

http://pan.baidu.com/s/1eQjMd0Y

 

相關文章