算法尝试:
一些好的算法出现,往往伴随着一些不那么好的算法。但是对于效果不太好的算法,它们普遍有一个共性,方便理解和实现。下面是通过一个循序渐进的方式来作一个简单地说明。
第一次尝试:朴素随机算法
这个算法很好理解,就是随机!每一次产生一个随机数,并加入集合。
[java] view plain copy
print?01.private void simpleRandom(int start, int end, int count) {
02. System.out.println("朴素随机算法:");
03. StringBuffer buffer = new StringBuffer();
04. for (int i = 0; i < count; i++) {
05. int random = NumberUtils.randomInteger(start, end);
06. buffer.append(i == 0 ? ("[" + random) : (", " + random));
07. }
08. buffer.append("]");
09. System.out.println(buffer);
10. }
第二次尝试:检查存在性随机算法
我们知道上面的方法有一个问题,就是可能会有重复数据。于是,我们就想到,在生成一个随机数的时候进行检查一下这个数是不是已经存在了,如果存在了就重新生成。
[java] view plain copy
print?01.private void checkRandom(int start, int end, int count) {
02. System.out.println("检查存在性随机算法:");
03. StringBuffer buffer = new StringBuffer();
04. List<Integer> save = new ArrayList<>();
05. for (int i = 0; i < count; i++) {
06. int random = NumberUtils.randomInteger(start, end);
07. if (exits(save, random)) {
08. i--;
09. continue;
10. }
11.
12. save.add(random);
13. buffer.append(i == 0 ? ("[" + random) : (", " + random));
14. }
15. buffer.append("]");
16. System.out.println(buffer);
17. }
第三次尝试:元素移除随机算法
上面的算法已经解决了数据重复的问题。不过,有一个很糟糕的问题就是可能我们要花费很长的时间来生成抽样随机数(这个要看脸了。。。。)。
不过,这里我们有了新想法。那就是在一个集合中去随机一个数,当这个被选中的时候就remove掉,那么下次再随机的时候是不是就不会再随机到这个数了?这样就很好地解决了随机数的重复问题。代码如下:
[java] view plain copy
print?01.private void removeRandom(int start, int end, int count) {
02. System.out.println("元素移除随机算法:");
03. StringBuffer buffer = new StringBuffer();
04. List<Integer> numbers = initList(start, end);
05. for (int i = 0; i < count; i++) {
06. int random = NumberUtils.randomInteger(count - i);
07. buffer.append(i == 0 ? ("[" + numbers.get(random)) : (", " + numbers.get(random)));
08. numbers.remove(random);
09. }
10.
11. buffer.append("]");
12. System.out.println(buffer);
13. }
第四次尝试:状态转移随机算法
在我之前的很多博客中,就有一些是算法中的状态转移过程。而状态的转移也是我最喜欢的算法之一。下面的图-1中标注了随机数的取值范围,序列中的橙色数字是结果中的随机序列。最下方的序列中有一些虚线的箭头,代表了状态的转移。
图-1 基于状态转移的抽样随机数生成算法
实现代码:
[java] view plain copy
print?01.private void statusRandom(int start, int end, int count) {
02. System.out.println("状态转移随机算法:");
03. StringBuffer buffer = new StringBuffer();
04. int[] status = new int[end + 1];
05. for (int i = 0; i < count; i++) {
06. int random = NumberUtils.randomInteger(start, end);
07. System.err.println(random);
08. if (status[random] == 0) {
09. buffer.append(i == 0 ? ("[" + random) : (", " + random));
10. status[random] = random == end ? start : (random + 1); // 不可能有在start之前的数字
11. } else {
12. // 状态转移
13. int index = random;
14. do {
15. index = status[index];
16. } while (status[index] != 0);
17.
18. buffer.append(i == 0 ? ("[" + index) : (", " + index));
19. status[index] = index == end ? start : (index + 1); // 不可能有在start之前的数字
20. }
21. }
22.
23. buffer.append("]");
24. System.out.println(buffer);
25. }
第五次尝试:递归Floyd随机算法
Floyd算法说到底也是一种状态的转移过程。该算法会要求输入一个List或是array来保存已经确定的随机数。顾名思义,这里我会用到递归的解法。在递归的过程中,我们把第i个随机数的状态转移到了第i-1个随机身上了。代码如下:
[java] view plain copy
print?01.private List<Integer> simpleFloyd(List<Integer> list, int count, int start, int end) {
02. if (count == 0) {
03. return list;
04. }
05. list = simpleFloyd(list, count - 1, start, end - 1);
06. int random = NumberUtils.randomInteger(start, end);
07. if (list.contains(random)) {
08. list.add(end);
09. } else {
10. list.add(random);
11. }
12. return list;
13. }
第六次尝试:迭代Floyd随机算法
思路与上面的递归Floyd随机算法是相似的,不过,这里我们加入了一个变量来做优化。就不需要再去递归了。代码如下:
[java] view plain copy
print?01.private List<Integer> iterationFloyd(int start, int end, int count) {
02. System.out.println("迭代Floyd随机算法:");
03. List<Integer> list = new ArrayList<>();
04. for (int i = end - count + 1; i < end; i++) {
05. int random = NumberUtils.randomInteger(start, i);
06. if (list.contains(random)) {
07. list.add(i);
08. } else {
09. list.add(random);
10. }
11. }
12.
13. return list;
14. }
关于生成抽样随机数
最新推荐文章于 2022-08-09 15:44:15 发布