大计算场景下 N个自然数的全排列问题

来源:互联网 发布:游戏优化软件 编辑:程序博客网 时间:2024/06/09 18:24

欢迎大家这么多年的捧场,为了更好地更舒服的写东西,思考东西,我决定从今天开始(20161017)把自己的所有文章和创作都逐步转移到自媒体上,请大家扫下面的二维码关注,接着收看更多有价值的干货

    

          最近看到庞果网上有个编程竞赛,题目是求N个自然数的全排列

               题目看起来很简单,但是要关注两个问题:

               1. 避免使用递归算法,因为这样会使计算复杂度成指数级别上升

               2. 巨大的计算量下,如何把计算工作切分成若干模块,并行。 现代计算机的特点是多核心,家用PC双核,四核,server上18路,36路核心。把大数据运算切分到各个核心上会事半功倍。

              

                因此我花了周六写了一个代码, 用非递归算法求全排列, 用特定的算法的把计算量分割成若干单元,用java线程池运算。 运算结果写本地文件。

               我的本地机器 2.93HZ双核CPU, 4G内存, win7系统,最高作了16个自然数的全排列,进行了一共4亿六千万次的运算,2分钟内完成。这个成绩还是可以的。

              

                更进一步,如果还想提高运算能力, 我会考虑用x86集群, 每个机器上启动一个JVM, 各个JVM之间tcp通信协同作业, 能将运算速度再提高一个数量级。

                有兴趣研究更深入算法或者技术实现的可以联系我,以下是我的代码:

               

                 #代码开源共享,但是转载的时候请注明出处,谢谢合作!!

                

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 */interface CombinationArg<E, T> extends Callable<T> {/** * 输出全排列的方法 在多JVM并行环境考虑打印到分散式文档,或者内存数据库中 *  * @param pn *            inputData */void printPermutation(ArrayList<E> pn);void setMax(ArrayList max);/** * 得到全排列的具体算法 有递归和非递归等多种算法实现 *  * @param pn *            inputData */boolean nextPermutation(ArrayList<E> pn);/** * 全排列API *  * @param pn *            inputData */void sortInt();void setPn(ArrayList pn);}/** * 非递归全排列组合算法 并且将该算法平均分割成若干任务,充分利用多核CPU并行运算 *  * @author 蒋彪 版权所有 转载请注明出处 * @param <E> * @mail nanjingjiangb@hotmail.com * @data 2013/3/4 */class CombinationArgNoRecursion<E> implements CombinationArg {private ArrayList pn = new ArrayList();// private ThreadLocal<WriteLog> write = new ThreadLocal();private WriteLog write = new WriteLogToFile();private SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss:SSS");private BigInteger max = new BigInteger("0");@Overridepublic void setPn(ArrayList pn) {this.pn = pn;}@Overridepublic void setMax(ArrayList max) {this.max = Util.changeArrayListToBigInteger(max);}public CombinationArgNoRecursion() {}@Overridepublic Object call() throws Exception {sortInt();return true;}private int firstFlg = 0;/** * 全排列数字 *  * @param num */public void sortInt() {System.out.println("/*****" + Thread.currentThread().getName()+ " 开始求全排列 ...... "+ formatter.format(new Date(System.currentTimeMillis()))+ "*******/");System.out.println("/*****************" + "start num is: "+ Util.changeArrayListToBigInteger(pn) + " end num is: " + max+ "***********************/");write.setWriter(Thread.currentThread().getName() + "打印全排序.txt");while (nextPermutation(pn)) {}write.closeWrite();System.out.println("/*****" + Thread.currentThread().getName()+ " 结束求全排列 ...... "+ formatter.format(new Date(System.currentTimeMillis()))+ "*******/");}/** * 在屏幕上打印出全排列 *  * @param str */@Overridepublic void printPermutation(ArrayList pn) {write.printPermutation(pn);}/** *  * 根据当前的排列p,计算下一个排列。 *  * 原则是从1234–>4321,若p已经是最后一个排列,传回false,否则传回true。 *  * @param pn *            计算起点 * @param max *            计算终点 */@Overridepublic boolean nextPermutation(ArrayList pn) {if (firstFlg == 0) {printPermutation(pn);firstFlg++;}int last = pn.size() - 1;int i, j, k;// 从后向前查找,看有没有后面的数大于前面的数的情况,若有则停在后一个数的位置。i = last;while (i > 0 && ((Integer) pn.get(i) < (Integer) pn.get(i - 1)))i--;// 若没有后面的数大于前面的数的情况,说明已经到了最后一个排列,返回false。if (i == 0)return false;// 从后查到i,查找大于p[i - 1]的最小的数,记入kk = i;for (j = last; j >= i; j--)if (((Integer) pn.get(j) > (Integer) pn.get(i - 1))&& ((Integer) pn.get(j) < (Integer) pn.get(k)))k = j;// 交换p[k]和p[i - 1]Collections.swap(pn, k, i - 1);// 倒置p[last]到p[i]for (j = last, k = i; j > k; j--, k++)Collections.swap(pn, j, k);printPermutation(pn);// 如果超出限制,则停止计算BigInteger now = Util.changeArrayListToBigInteger(pn);if (now.compareTo(max) > 0) {return false;}return true;}}interface WriteLog {void writeLog(String log);void printPermutation(ArrayList pn);void setWriter(String fileName);void closeWrite();}class WriteLogToFile implements WriteLog {ThreadLocal<FileWriter> writer = new ThreadLocal();public void setWriter(String fileName) {try {writer.set(new FileWriter(fileName, true));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public void closeWrite() {try {writer.get().close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}/** * B方法追加文件:使用FileWriter *  * @param fileName * @param content */public void appendMethod(String content) {try {// 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件writer.get().write(content);// 避免频繁的openWrite, closeWrite,增加CPU负担// writer.close();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void writeLog(String log) {appendMethod(log);appendMethod("\r\n");}@Overridepublic void printPermutation(ArrayList pn) {StringBuffer tmp = new StringBuffer();for (int i = 0; i < pn.size(); i++) {tmp.append(pn.get(i)).append(",");}writeLog(tmp.toString());}}class Util {public static ArrayList<Integer> changeStringsToArray(String[] s) {ArrayList<Integer> px = new ArrayList<Integer>(s.length);for (String x : s) {px.add(Integer.parseInt(x));}return px;}public static BigInteger changeArrayListToBigInteger(ArrayList<Integer> pn) {StringBuffer orgData = new StringBuffer();for (int j = 0; j < pn.size(); j++) {orgData.append(pn.get(j));}return new BigInteger(orgData.toString());}public static ArrayList<Integer> changeBigIntegerToArrL(BigInteger tmp) {String x = tmp.toString();ArrayList<Integer> r = new ArrayList<Integer>();for (int j = 0; j < x.length(); j++) {r.add(Integer.parseInt(String.valueOf(x.charAt(j))));}return r;}}


 

原创粉丝点击