二进制搜索在较小的数组上很慢

Binary search slow on smaller arrays

我写了一个程序来测试线性和二进制搜索的速度,发现在排序数组的大小为 1000 的开始时,二进制搜索比后来数组大小增加时使用更多的时间。有没有解释为什么会这样。

程序检查算法 1000 次并计算为每个数组找到所需项目所花费的平均时间,大小为 n,包含从 1 到 n 的元素。

import java.util.*;

public class Test {
public static void main(String[] args) {
    System.out.println("   n     |    linear    |   binary    |\n---------+--------------+---------------");

    for (int i = 1000; i <= 100000; i += 1000) {
        System.out.printf("%9d|%14d|%14d|\n", i, timeLinear(i), timeBinary(i));
    }
}

static int[] generateTable(int n) {
    int[] a = new int[n];
    int ind = 0;

    for (int i = 1; i <= n; i++) {
        a[ind++] = i;
    }

    return a;
}

static int findLinear(int[] a, int n) {
    for (int i = 0; i < a.length; i++) {
        if (a[i] == n) return i;
    }
    return -1;
}

static int findBinary(int[] a, int l, int r, int n) {
    if (l > r) return -1;
    int mid = l + (r - l) / 2;
    if (n < a[mid]) {
        findBinary(a, l, mid - 1, n);
    }
    else if (n > a[mid]) {
        findBinary(a, mid + 1, r, n);
    }
    return mid;
}

static long timeLinear(int n) {
    Random rn = new Random();
    int[] arr = generateTable(n);

    long start = System.nanoTime();

    for (int i = 0; i < 1000; i++) {
        findLinear(arr, rn.nextInt(n) + 1);
    }

    long stop = System.nanoTime() - start;
    return stop / 1000;
}

static long timeBinary(int n) {
    Random rn = new Random();
    int[] arr = generateTable(n);

    long start = System.nanoTime();

    for (int i = 0; i < 1000; i++) {
        findBinary(arr, 0, arr.length - 1, rn.nextInt(n) + 1);
    }

    long stop = System.nanoTime() - start;
    return stop / 1000;
}

}

输出:

   n     |    linear    |   binary     |
---------+--------------+---------------
     1000|          5736|          1518|
     2000|           787|          2012|
     3000|          1313|           626|
     4000|          1230|           711|
     5000|          1281|           723|
     6000|          1888|           463|
     7000|          1846|           213|
     8000|          1089|           187|
     9000|          1213|           188|
    10000|          1583|           216|
    11000|          1607|           302|
    12000|          3294|           311|
    13000|          3656|           274|
    14000|          3497|           274|
    15000|          2315|           141|
    16000|          1945|           135|
    17000|          2223|           154|
    18000|          2964|           136|
    19000|          2464|           138|
    20000|          2472|           138|
    21000|          2829|           140|
    22000|          3209|           157|
    23000|          2901|           141|
    24000|          3095|           140|
    25000|          3157|           142|
    26000|          3235|           162|
    27000|          4118|           143|
    28000|          3483|           145|
    29000|          3906|           144|
    30000|          3801|           145|
    31000|          4322|           166|
    32000|          4057|           146|
    33000|          4516|           167|
    34000|          4566|           147|
    35000|          4453|           148|
    36000|          4708|           148|
    37000|          4772|           149|
    38000|          5391|           168|
    39000|          5500|           150|
    40000|          5048|           150|
    41000|          4979|           150|
    42000|          5402|           151|
    43000|          6160|           171|
    44000|          6402|           153|
    45000|          5855|           152|
    46000|          5702|           152|
    47000|          6184|           153|
    48000|          5963|           154|
    49000|          6927|           155|
    50000|          6611|           154|
    51000|          6326|           155|
    52000|          6488|           155|
    53000|          7690|           156|
    54000|          7245|           156|
    55000|          7074|           156|
    56000|          6998|           154|
    57000|          8299|           157|
    58000|          7456|           156|
    59000|          7776|           156|
    60000|          8015|           189|
    61000|          7762|           160|
    62000|          8145|           158|
    63000|          7946|           158|
    64000|          9157|           156|
    65000|          8299|           157|
    66000|          8399|           159|
    67000|          9119|           159|
    68000|          8758|           159|
    69000|          8921|           161|
    70000|          9366|           162|
    71000|          9326|           170|
    72000|          9035|           161|
    73000|          9873|           189|
    74000|          9642|           189|
    75000|         10272|           185|
    76000|         10299|           163|
    77000|         10602|           162|
    78000|          9878|           165|
    79000|         10790|           168|
    80000|         10535|           165|
    81000|         10627|           164|
    82000|         11579|           166|
    83000|         11003|           167|
    84000|         11778|           164|
    85000|         10686|           167|
    86000|         11280|           168|
    87000|         12562|           171|
    88000|         11190|           197|
    89000|         12949|           167|
    90000|         11954|           169|
    91000|         13170|           168|
    92000|         12013|           169|
    93000|         12245|           170|
    94000|         12949|           194|
    95000|         12264|           172|
    96000|         13754|           170|
    97000|         12919|           171|
    98000|         14172|           170|
    99000|         13436|           172|
   100000|         14466|           171|

你应该给JIT编译器一个加热的机会。试试这个:

for (int j = 0; j < 20; j++) {
    System.out.printf("%n%nCycle # %d%n%n%n%n", j);
    for (int i = 1000; i <= 100000; i += 1000) {
        System.out.printf("%9d|%14d|%14d|\n", i, timeLinear(i), timeBinary(i));
    }
}

较小的列表仅由 1 或 2 个 cpu 内核执行,但较大的列表 bing 使用了所有内核,因此速度更快。