本文由左程云老师讲授个人整理总结
对数器
在没法OJ,或OJ也有毛病的时候,我们的算法可能大致是正确的,能简单的通过几个样例,但遇到复杂庞大的样例时抛锚,我们对着冗长的代码,去调试,用人工写样例的方式去测试是愚蠢的,这时候我们需要对数器出场了
什么是对数器
对数器是通过用大量测试数据来验证算法是否正确的一种方式
对数器需要两样东西:
- 绝对正确的方法
- 能产生大量随机样例的随机发生器
问题来了,有绝对正确的方法了为什么不用呢,我们不要求绝对正确的算法的时间复杂度和空间复杂度,我们希望用它来证明我们的算法是正确的,这样我们才能确保如何找出我们自己的算法所出现的错误
对数器的使用
- 有一个你想要测的方法a
- 实现一个绝对正确但是复杂度不好的方法b
- 实现一个随机样本产生器
- 实现比对的方法
- 把方法a和方法b比对很多次来验证方法a是否正确
- 如果有一个样本使得比对出错,打印样本分析是哪个方法出错
- 当样本数量很多时比对测试依然正确,可以确定方法a已经正确
注意事项:通常想要测试的方法a是时间复杂度低的优秀的算法,随机样本发生器产生的样本量应可能100000+,这样才能保证样本状况的全覆盖,样本大小要小,这样出错时才会好比对查出错误。
对数器编写
按步骤
第一步: 有一个你想要测试的方法a
以上一篇书写的插入排序为例:
function InsertionSort(arr){
if(arr==null||arr.length<2){return;}
for(var i=1;i<arr.length;i++){
for(var j=i-1;j>=0&&arr[j]>arr[j+1];j--){
swap(arr,j,j+1);
}
}
return arr;
}
function swap(arr,i,j){
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
第二步: 实现一个绝对正确但是复杂度不好的方法b
选择编程语言自带的数组排序方法
sort方法如果不传入参数要求,则数组是按照字符编码的顺序进行排序。
例如:
var arr = new Array(6)
arr[0] = "10"
arr[1] = "5"
arr[2] = "40"
arr[3] = "25"
arr[4] = "1000"
arr[5] = "1"
document.write(arr + "<br />")
document.write(arr.sort())
输出结果是
10,5,40,25,1000,1
1,10,1000,25,40,5
因此如果想按照数值大小排序的话则需要使用一个排序函数:
function sortNumber(a,b){
return a - b
}
document.write(arr.sort(sortNumber))
输出结果是 1,5,10,25,40,1000
最终我们书写一个绝对正确的方法:
function sortNumber(a,b){
return a - b
}
function rightMethod(arr) {
arr.sort(sortNumber);
}
备注:arr是随机样本生成器产生的随机数组
第三步: 实现一个随机样本产生器
function generateRandomArray(maxSize, maxValue) {
var arr = new Array(Math.floor((maxSize + 1) * Math.random()));
for (var i = 0; i < arr.length; i++) {
arr[i] = Math.floor((maxValue + 1) * Math.random())-Math.floor(maxValue * Math.random());
}
return arr;
}
代码说明
- 生成长度随机[0, size]的数组
- 一个随机数减去另一个随机数,生成[-value, value]的随机数
综上将生成长度随机值也随机的数组
第四步: 把方法a和方法b比对很多次来验证方法a是否正确
比对方法
function isEqual(arr1, 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 (let i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false
}
}
return true;
}
拷贝数组
function copyArray(arr) {
if (arr == null) {
return null;
}
return [].concat(arr);
}
大样本量测试
function Test() {
var testTimes = 100000;
var maxSize = 10;
var maxValue = 100;
var succeed = true;
for (var i = 0; i < testTimes; i++) {
var arr1 = generateRandomArray(maxSize,maxValue);
var arr2 = copyArray(arr1);
var arr3 = copyArray(arr1);
InsertionSort(arr1);
rightMethod(arr2);
if (!isEqual(arr1, arr2)) {
succeed = false;
console.log(arr3);
break;
}
}
console.log(succeed ? "Good job!" : "Damn it!");
}
Test();
代码说明
testTimes为我们将要进行测试的样本数量,100000次几乎穷尽了可能出现的可能
arr1是随机样本生成器生成的数组,通过拷贝为三份,它们大小数值都相等,但它们是不同的数组
arr1用来测试写的插入排序,arr2用来进行绝对正确检测
如果十万组样本arr1和arr2相等,并且打印出Good job说明arr1完全正确
测试结果
通过测试!!哇哦
对数器完整代码(测试插入排序为例)
function InsertionSort(arr){
if(arr==null||arr.length<2){return;}
for(var i=1;i<arr.length;i++){
for(var j=i-1;j>=0&&arr[j]>arr[j+1];j--){
swap(arr,j,j+1);
}
}
return arr;
}
function swap(arr,i,j){
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
function sortNumber(a,b){
return a - b
}
function rightMethod(arr) {
arr.sort(sortNumber);
}
function generateRandomArray(maxSize, maxValue) {
var arr = new Array(Math.floor((maxSize + 1) * Math.random()));
for (var i = 0; i < arr.length; i++) {
arr[i] = Math.floor((maxValue + 1) * Math.random())-Math.floor(maxValue * Math.random());
}
return arr;
}
function isEqual(arr1, 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 (let i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false
}
}
return true;
}
function copyArray(arr) {
if (arr == null) {
return null;
}
return [].concat(arr);
}
function Test() {
var testTimes = 10000;
var maxmaxSize = 10;
var maxValue = 100;
var succeed = true;
for (var i = 0; i < testTimes; i++) {
var arr1 = generateRandomArray(maxmaxSize,maxValue);
var arr2 = copyArray(arr1);
var arr3 = copyArray(arr1);
InsertionSort(arr1);
rightMethod(arr2);
if (!isEqual(arr1, arr2)) {
succeed = false;
console.log(arr3);
break;
}
}
console.log(succeed ? "Good job!" : "Damn it!");
}
Test();