4.2 猴子排序:概率论的幽默诠释

历史背景

猴子排序(Bogo Sort)是一种幽默的排序算法,其名称来源于"无限猴子定理"——如果一只猴子在打字机上随机按键,最终会打出莎士比亚全集。猴子排序也被称为"愚蠢排序"(Stupid Sort)或"随机排序"(Random Sort)。它不是实际使用的排序算法,而是用来演示算法复杂度分析的反面教材。

算法原理

猴子排序的算法极其简单:

  1. 检查数组是否有序
  2. 如果有序,结束
  3. 如果无序,随机打乱数组
  4. 回到步骤 1

Java 实现

java
复制代码
import java.util.Random;

public class BogoSort {
    
    /**
     * 猴子排序
     * 警告:此算法平均时间复杂度为 O((n+1)!),仅用于教学演示
     * 对于超过 10 个元素的数组,可能需要数小时甚至数天才能完成排序
     */
    public static void sort(int[] arr) {
        Random random = new Random();
        int attempts = 0;
        
        while (!isSorted(arr)) {
            shuffle(arr, random);
            attempts++;
            
            // 安全保护,避免无限循环
            if (attempts > 1_000_000) {
                System.out.println("超过一百万次尝试,放弃排序");
                return;
            }
        }
        
        System.out.println("排序完成,共尝试 " + attempts + " 次");
    }
    
    /**
     * 检查数组是否有序
     */
    private static boolean isSorted(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            if (arr[i] > arr[i + 1]) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * Fisher-Yates 洗牌算法
     */
    private static void shuffle(int[] arr, Random random) {
        for (int i = arr.length - 1; i > 0; i--) {
            int j = random.nextInt(i + 1);
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    
    /**
     * 猴子排序(泛型版本)
     */
    public static <T extends Comparable<T>> void sortGeneric(T[] arr) {
        Random random = new Random();
        int attempts = 0;
        
        while (!isSortedGeneric(arr)) {
            shuffleGeneric(arr, random);
            attempts++;
            if (attempts > 1_000_000) {
                System.out.println("超过一百万次尝试,放弃排序");
                return;
            }
        }
        System.out.println("排序完成,共尝试 " + attempts + " 次");
    }
    
    private static <T extends Comparable<T>> boolean isSortedGeneric(T[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            if (arr[i].compareTo(arr[i + 1]) > 0) {
                return false;
            }
        }
        return true;
    }
    
    private static <T> void shuffleGeneric(T[] arr, Random random) {
        for (int i = arr.length - 1; i > 0; i--) {
            int j = random.nextInt(i + 1);
            T temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
}

效率分析

指标 最好情况 最坏情况 平均情况
时间复杂度 O(n)(运气极好) 无界(可能永远不结束) O((n+1)!)
空间复杂度 O(1) O(1) O(1)
比较次数 n-1 无界 n(n+1)!
交换次数 0 无界 n(n+1)!

猴子排序是不稳定的排序算法,平均时间复杂度为 O((n+1)!)。对于 10 个元素的数组,平均需要约 4000 万次尝试。它是算法复杂度分析中最经典的反例。

应用场景

  • 教学演示(展示什么是"坏"算法)
  • 算法复杂度分析的反面教材
  • 幽默和娱乐
  • 随机性演示