使用三种方法求解前N个正整数的排列
2016-04-02 21:17
357 查看
本篇博文给大家介绍前N个正整数的排列求解的三种方式。第一种是暴力求解法;第二种则另外声明了一个长度为N的数组,并且将已经排列过的数字保存其中;第三种方式则采用了另外一种思路,即首先获取N个整数的升序排列,然后对其位置进行随机交换以达到前N个整数的随机排列的目的。首先我们来看看第一种方式,具体代码如下:
这里输出结果如下
即当n为100000的时候,计算结果需要38.580s。该算法通过readInt(int i, int j)方法获取一个大于等于i并且小于等于j的整数,通过isExist(int[] arr, int index)方法判断数组中在索引为index之前的序列里是否已经存在了索引为index对应的值。该算法求解的思路即为从0开始对数组arr赋值,并且在每次获取一个随机数之后判断数组之前的部分中是否已经存在了该值,若存在了该值则继续获取,直至数组之前的部分中不存在该值。这种算法将产生大量的重复计算,主要来源在于判断数组之前的部分中是否存在该值和对于新的数组元素的获取两个部分。为了解决第一个问题,即判断数组之前部分中是否存在该值,我们采用了第二种算法,具体代码如下:
计算结果如下:
若将N改为1000000,计算结果为:1.648s,若将N改为10000000,计算结果为33.679s,可以看出,该算法相对于第一种算法有所改进,但是对于千万级数据量的处理消耗时长较长。第二种算法另外声明了一个数组used,该数组长度为N,这里将要求解的数据(长度为N的正整数的排列)与used数组的下标形成一种一一对应的关系,当要判断某个获取到的正整数是否已经存在时,只需要判断数组中对应下标的数据值是否为true,为true则表示已经存在。但是这里经过我们的分析也会发现,对于采用readInt(int i, int j)方法获取新数据时,若该数据非常靠后,那么获取的难度(循环的次数)将会大大增加,这里也会影响该算法的运行效率。为了避免这个问题,我们则可以采用第三种方法,该方法则使用了一个已知的事实,即结果数组中的数值我们是完全确定的,变化的只是该数值的顺序,因而,我们如果采用随机算法改变各个数值的位置,即可达到排列各个数值的目的。具体的算法如下:
运行结果如下:
该方法通过readInt(int i, int j)方法每次获取数组中索引为0到索引为i的随机索引,将并且将数组中索引为i的数据与获取到的索引对应的数据位置替换,这样就可以达到全排列的目的。
import java.util.Random; import org.junit.Test; public class App { private int n = 100000; @Test public void testFirst() { int[] arr = new int ; long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { arr[i] = readInt(1, n + 1); while (isExist(arr, i)) { arr[i] = readInt(1, n + 1); } } long end = System.currentTimeMillis(); System.out.println("方法一消耗时长为:" + (end - start)); } private boolean isExist(int[] arr, int index) { for (int i = 0; i < index; i++) { if (arr[i] == arr[index]) { return true; } } return false; } private int readInt(int i, int j) { Random random = new Random(); int result = random.nextInt(j); while (result < i) { result = random.nextInt(j); } return result; } }
这里输出结果如下
方法一消耗时长为:38580
即当n为100000的时候,计算结果需要38.580s。该算法通过readInt(int i, int j)方法获取一个大于等于i并且小于等于j的整数,通过isExist(int[] arr, int index)方法判断数组中在索引为index之前的序列里是否已经存在了索引为index对应的值。该算法求解的思路即为从0开始对数组arr赋值,并且在每次获取一个随机数之后判断数组之前的部分中是否已经存在了该值,若存在了该值则继续获取,直至数组之前的部分中不存在该值。这种算法将产生大量的重复计算,主要来源在于判断数组之前的部分中是否存在该值和对于新的数组元素的获取两个部分。为了解决第一个问题,即判断数组之前部分中是否存在该值,我们采用了第二种算法,具体代码如下:
import java.util.Random; import org.junit.Test; public class App { private int n = 100000; @Test public void testSecond() { int[] arr = new int ; long start = System.currentTimeMillis(); boolean[] used = new boolean ; for (int i = 0; i < n; i++) { arr[i] = readInt(1, n + 1); while (used[arr[i] - 1]) { arr[i] = readInt(1, n + 1); } used[arr[i] - 1] = true; } long end = System.currentTimeMillis(); System.out.println("方法二消耗时长为:" + (end - start)); } private int readInt(int i, int j) { Random random = new Random(); int result = random.nextInt(j); while (result < i) { result = random.nextInt(j); } return result; } }
计算结果如下:
方法二消耗时长为:148
若将N改为1000000,计算结果为:1.648s,若将N改为10000000,计算结果为33.679s,可以看出,该算法相对于第一种算法有所改进,但是对于千万级数据量的处理消耗时长较长。第二种算法另外声明了一个数组used,该数组长度为N,这里将要求解的数据(长度为N的正整数的排列)与used数组的下标形成一种一一对应的关系,当要判断某个获取到的正整数是否已经存在时,只需要判断数组中对应下标的数据值是否为true,为true则表示已经存在。但是这里经过我们的分析也会发现,对于采用readInt(int i, int j)方法获取新数据时,若该数据非常靠后,那么获取的难度(循环的次数)将会大大增加,这里也会影响该算法的运行效率。为了避免这个问题,我们则可以采用第三种方法,该方法则使用了一个已知的事实,即结果数组中的数值我们是完全确定的,变化的只是该数值的顺序,因而,我们如果采用随机算法改变各个数值的位置,即可达到排列各个数值的目的。具体的算法如下:
import java.util.Random; import org.junit.Test; public class App { private int n = 10000000; @Test public void testThird() { int[] arr = new int ; long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { arr[i] = i + 1; } for (int i = 1; i < n; i++) { swapReferences(arr, i, readInt(0, i)); } long end = System.currentTimeMillis(); System.out.println("方法三消耗时长为:" + (end - start)); } private void swapReferences(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } private int readInt(int i, int j) { Random random = new Random(); int result = random.nextInt(j); while (result < i) { result = random.nextInt(j); } return result; } }
运行结果如下:
方法三消耗时长为:2105
该方法通过readInt(int i, int j)方法每次获取数组中索引为0到索引为i的随机索引,将并且将数组中索引为i的数据与获取到的索引对应的数据位置替换,这样就可以达到全排列的目的。
相关文章推荐
- 控制台,终端,虚拟终端,tty,shell等概念的区别
- C++第二次上机实验
- MFC和VC++写的电脑关机小程序(源码+下载)
- 你在公司项目里面看到过哪些操蛋的代码?
- windows7 professional.iso
- C结构
- NYOJ999 师傅又被妖怪抓走了
- 自己的密码该如何设计才能预防被撞库呢?这里给个参考。
- 去除SVN标识
- JavaScript面向对象焦点图片轮播banner
- 自定义坐标来放置你的控件
- Elasticsearch(二)集群设置
- 笔试总结——数据库篇【持续更新】
- vm cnetos 配置
- 字符界面安装CentOS 6.5操作系统
- 39.leetcode题目121、 122、 123
- 一起talk C栗子吧(第一百三十六回:C语言实例--exec系列函数二)
- Android shape的使用
- 版本管理
- HDU5655 CA Loves Stick (BC)