對數器的作用
對數器是通過用大量測試數據來驗證算法是否正確的一種方式。在算法筆試的時候,我們經常只能確定我們寫出的算法在邏輯上是大致正確的,但是誰也不能一次性保證絕對的正確。特別是對於一些複雜的題目,例如貪心算法,我們往往無法在有限時間內用數學公式來推導證明我們程序的正確性。而且在線的OJ一般只會給出有數的幾個簡單的samples
,可能我們的算法在這些簡單的samples
偶然通過了,但是在一些複雜的samples
處卻出現了問題。這時我們無法使用複雜的samples
來分析調試我們的代碼,人工設計樣例來測試代碼的效率又太低,而且不能確保考慮各種特殊情況。因此,能隨機產生不同情況的數據的對數器就派上了用場。
對數器,簡而言之,就是一個絕對正確的方法和能產生大量隨機樣例的隨機器的組合。看到這裏,有些童鞋有疑問了。既然我們知道了絕對正確的方法,直接用這個方法不就好了嘛?
請注意,算法追求的不是解決問題,而是高效率的解決問題。對於一個數組的排序,如果筆試中要求的時間複雜度是$O(NlogN)$$,但是你卻寫了一個冒泡排序的算法交上去了,這時OJ就會提示:
Time Limit Exceeded
而在對數器中,我們要求的絕對正確的算法是沒有時間和空間複雜度的限制的,唯一的要求是確保絕對正確。因爲只有絕對正確,我們才能通過樣例的比對,發現我們的代碼是在哪裏出了錯誤。
相關概念
- 有一個你想要測的方法
a
; - 實現一個絕對正確但是複雜度不好的方法
b
; - 實現一個隨機樣本產生器;
- 實現對比算法
a
和b
的方法; - 把方法
a
和方法b
比對多次來驗證方法a
是否正確; - 如果有一個樣本使得比對出錯,打印樣本分析是哪個方法出錯;
- 當樣本數量很多時比對測試依然正確,可以確定方法
a
已經正確。
其中要注意以下幾點:
- 隨機產生的樣本應該是小數據集,但是要進行多次(10w+)的對比。小數據集是因爲方便對比分析,多次比對是要覆蓋所有的隨機情況。
- 算法
b
要保持正確性。
示例
冒泡排序的對數器:
-
要測的方法
public static void bubbleSort(int[] arr) { if (arr==null || arr.length < 2) return; for (int end = arr.length - 1;end>0; end--) { for (int i = 0; i < end; i++) { if (arr[i] > arr[i+1]) swap(arr, i, i+1); } } }
-
實現一個絕對正確,可以複雜度不是很好的方法
b
// 可以直接用一些庫函數來進行測試 public static void rightMethod(int[] arr) { Arrays.sort(arr); }
-
實現一個隨機樣本產生器
// 隨機數生成器 public static int[] generateRandomArray(int size, int value) { //Math.random() -> double [0,1) //(int) ((size + 1) * Math.random()) -> [0,size]整數 // 生成長度隨機[0, size]的數組 int[] arr = new int[(int) ((size+1) * Math.random())]; for (int i = 0; i < arr.length; i++) { // 一個隨機數減去另一個隨機數,生成[-value, value]的隨機數 arr[i] = (int) ((value+1) * Math.random()) - (int) (value * Math.random()); } return arr; }
-
實現比對的方法
// 判斷兩個數組是否相等 public static boolean isEqual(int[] arr1, int[] arr2) { if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) return false; if (arr1 == null && arr2 == null) return true; if (arr1.length != arr2.length) return false; for (int i = 0; i < arr1.length; i++) { if (arr1[i] != arr2[i]) return false; } return true; }
- 將方法
a
和方法b
進行多次的比對 - 如果有一個樣本使得本次比對出錯,則打印該樣本,分析方法錯在哪裏
-
當樣本數量足夠多時,比對測試依然正確,則可以確定我們寫的方法
a
是正確的public static void main(String[] args) { int testTime = 500000; int size = 10; int value = 100; boolean succeed = true; for (int i = 0; i < testTime; i++) { int[] arr1 = generateRandomArray(size, value); int[] arr2 = copyArray(arr1); int[] arr3 = copyArray(arr1); bubbleSort(arr1); rightMethod(arr2); if (!isEqual(arr1, arr2)) { succeed = false; printArray(arr3); break; } } System.out.println(succeed ? "Nice!" : "error----"); int[] arr = generateRandomArray(size, value); printArray(arr); bubbleSort(arr); printArray(arr); }
小提示
很多童鞋進行筆試前,都是背一些記在小本本上的代碼,然後匆匆上陣。寫出的算法的正確性完全靠OJ的判斷,當程序卡在一個2000行的數組樣例處出現錯誤時,就完全傻了......這T喵叫我怎麼去進行調試分析啊。而有對數器的小夥伴就不一樣了,由於使用的都是小樣本,出現錯誤時也方面進行分析。而且進行了多次測試,確保覆蓋了所有的特殊情況。因此筆試前我們可以準備一些對數器模版,如數組排序的對數器,鏈表的對數器等等。後續我也會更新一些對數器的模版,有java
版本和python
版本。
記住哦,offer都是留給有準備的人~~
數組排序
後續版本,陸續更新中.....