题目看起来很简单,但是要关注两个问题:
1. 避免使用递归算法,因为这样会使计算复杂度成指数级别上升
2. 巨大的计算量下,如何把计算工作切分成若干模块,并行。 现代计算机的特点是多核心,家用PC双核,四核,server上18路,36路核心。把大数据运算切分到各个核心上会事半功倍。
因此我花了周六写了一个代码, 用非递归算法求全排列, 用特定的算法的把计算量分割成若干单元,用java线程池运算。 运算结果写本地文件。
我的本地机器 2.93HZ双核CPU, 4G内存, win7系统,最高作了16个自然数的全排列,进行了一共4亿六千万次的运算,2分钟内完成。这个成绩还是可以的。
更进一步,如果还想提高运算能力, 我会考虑用x86集群, 每个机器上启动一个JVM, 各个JVM之间tcp通信协同作业, 能将运算速度再提高一个数量级。
有兴趣研究更深入算法或者技术实现的可以联系我,以下是我的代码:
#代码开源共享,但是转载的时候请注明出处,谢谢合作!!
[java]
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 非递归算法 + 分割成若干计算单位并发运算
* 在高计算量下会有明显优势,但是在计算量较小,线程切换会有开销
* 本地机器 2.93HZ双核CPU, 4G内存, win7系统, 16个数的全排列,一共4亿7千万次运算,2分钟内完成
* 如果更进一步,可以考虑用x86机器做集群运算,每个机器上启动一个JVM, JVM之间用tcp通信协调,一起跑
* 有兴趣想深入研究可以联系我
*
* @author 蒋彪 版权所有 转载请注明出处
* @param <E>
* @param <T>
* @mail nanjingjiangb@hotmail.com
* @data 2013/3/4
*/
public class CombinationTest<E> {
// 计算模块的线程执行框架,线程数保持在CPU核数 + 1, 可以最大榨取机器性能
// private final static ExecutorService exec = Executors
// .newFixedThreadPool(1);
private final static ExecutorService exec = Executors.newCachedThreadPool();
public static void main(String[] args) {
if (args.length < 1) {
return;
}
// 先将这串数字正排序,预先处理
Arrays.sort(args);
ArrayList<Integer> min = Util.changeStringsToArray(args);
Arrays.sort(args, Collections.reverseOrder());
ArrayList<Integer> max = Util.changeStringsToArray(args);
ArrayList diffBlock = getStepData(min, max);
for (int k = 0; k < diffBlock.size() - 1; k++) {
CombinationArg task = new CombinationArgNoRecursion();
task.setPn((ArrayList<Integer>) diffBlock.get(k));
task.setMax((ArrayList<Integer>) diffBlock.get(k + 1));
Future future = exec.submit(task);
// future.get();
}
// exec.shutdown();
}
/**
* 分割待排序数据,分割成若干个起点的小块计算量 算法待优化,可考虑CPU,内存等限制计算快的大小
*
* @param pn
* @return
*/
public static ArrayList getStepData(ArrayList<Integer> min,
ArrayList<Integer> max) {
ArrayList result = new ArrayList();
// 循环把第二位和第一位交换
// 比如,原始数据是1,2,3 ,4, 5
// 那么可以分割成 12345 ~ 21345 ~ 31245~ 41235~ 51234 几个近似区间来计算
result.add((ArrayList) min.clone());
for (int j = 1; j < max.size(); j++) {
Collections.swap(min, j, 0);
result.add((ArrayList) min.clone());
}
result.add(max);
return result;
}
}
/**
* 全排列组合算法接口 继承自Callable,方便在多线程环境下扩展
*
* @author 蒋彪 版权所有 转载请注明出处
* @param <E>
* @param <T>
* @mail nanjingjiangb@hotmail.com
* @data 2013/3/4
*/
inte易做图ce CombinationArg<E, T> extends Callable<T> {
/**
* 输出全排列的方法 在多JVM并行环境考虑打印到分散式文档,或者内存数据库中
*
* @param pn
* inputData
*/
void printPermutation(ArrayList<E> pn);
void setMax(ArrayList max);
/**
* 得到全排列的具体算法 有递归和非递归等多种算法实现
*
* @param pn