前提
之前在知乎上看见一个有意思的排序算法——睡排序。
睡排序最早好像是4chan上一个用户用shell脚本实现的:
算法思想简洁明了:利用进程的sleep来实现 越大的数字越迟输出。
虽然像2L说的那样,这个算法没什么利用价值。但我打算试着用Java来实现一下这个“睡排序”。
Java实现
既然选择用Java来实现,我们就没必要为数组中每一个元素启一个进程,启多线程就够了。
Java启线程的方式有很多:1.继承Thread 2.实现Runnable 3.实现Callable ...
而当前需求是要同时启固定数量的多个线程,那么通过Executor提供的线程池来启线程最合适不过了。
下面是代码:
public static List<Integer> sleepSort(int[] nums){ List<Integer> res = new Vector<>(); // 1 ExecutorService executor = Executors.newFixedThreadPool(nums.length); IntStream.of(nums).forEach(i -> executor.execute(() -> { try { Thread.sleep(i * 200); // 2 } catch (InterruptedException e) { e.printStackTrace(); } res.add(i); })); executor.shutdown(); while (true){ // 3 if (executor.isTerminated()) break; } return res; }
几个注意点:
1. 作为一个排序方法只是打印结果的话未免有些单调,所以我打算返回一个List作为排序后的结果。因为涉及多线程的操作,这里选择线程安全的Vector。
2. forEach的遍历方式会使各个线程的启动时间有细微的差距,因此sleep的时间不能太多。经过测试,* 200(ms) 比较合适。
3. 在返回结果之前,必须保证所有线程执行完毕。注意 "executor.shutdown()" 只是关闭线程池,并不会终止线程。所以要通过 "executor.isTerminated()" 来判断。
算法分析
这个算法看起来的复杂度是O(nums.length)。 实际上,复杂度为O(n ^ 2 ),因为维护多个后台线程程依赖于CPU来管理进程的上下文切换和优先级,所以该算法基本上将实际排序外包给了CPU。
测试
写一个生成乱序数组的方法,用于生成测试用例。
public static int[] getRandomArray(int len, int maxNum){ int[] res = new int[len]; Random random = new Random(); for (int i=0;i<len;i++) { res[i] = random.nextInt(maxNum); } return res; } public static void main(String[] args){ System.out.println(sleepSort(getRandomArray(18, 29))); }
结果:
理论上数组的最大长度为当前JVM能创建的最大线程数:(系统CPU内存- JVM内存- 系统预留内存) / (线程栈的大小)
最后
该算法仅供娱乐,如何什么可以改进的地方,欢迎讨论。