Java 随机数之从指定数据范围内随机选取n个不重复的数据
一、简述
记--从指定数据范围内随机选取n个不重复的数据,如从1~100中随机选取10个数据。
二、效果
三、工程结构
四、源文件
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Scanner;
import java.lang.Integer;
public class RandomTest {
static List<Integer> mBaseList = null;
//从[minVal, topVal]范围中选出cnt个不重复的数据
public static List<Integer> genUniqueRandomVal(int minVal, int topVal, int cnt){
int index;
int size = topVal-minVal+1;
if (minVal >= topVal){
return null;
}
if (cnt>size){
return null;
}
if (null == mBaseList){
mBaseList = new ArrayList<Integer>();
}
//初始化基本数据集合
mBaseList.clear();
for (int i = minVal; i <= topVal; i++){
mBaseList.add(i);
}
List<Integer> uniqueValList = new ArrayList<Integer>();//无重复的数据集合
Random random = new Random();
for (; cnt > 0;){
index = random.nextInt(size);//范围[0, size)
uniqueValList.add(mBaseList.get(index));//添加到数据集合
mBaseList.remove(index);//基本数据集合移除已经加到uniqueValList的数据,这样子就不会重复
cnt--;
size--;
}
return uniqueValList;
}
public static void main(String[] args) {
System.out.println("Please input min,max,count:");
Scanner scan = new Scanner(System.in);
//从输入流中获取三个数值
int min = Integer.parseInt(scan.next());
int max = 0;
int cnt = 0;
if(scan.hasNext()){
max = Integer.parseInt(scan.next());
if(scan.hasNext()){
cnt = Integer.parseInt(scan.next());
List<Integer> list = RandomTest.genUniqueRandomVal(min, max, cnt);
for(Integer i : list) {//遍历打印得到的数据集合
System.out.print(i+" ");
}
}
}
System.out.println("\nFinish.");
}
}
五、分析
指定范围内的随机数是会有重复的可能,并且小范围多次随机时重复率很高,为了快速生成不重复的随机数,我们需要保证每次得到的随机数是不一样的。如果生成的随机数是重复的,那么再生成一次,直到不重复。这个方法是不可取的,当选取数很接近数据总数时候,重复率是非常大的,如1~100中随机选取99不同的数时,可能需要生成次数远远大于99次,需要消耗很长时间或者几乎不能生成;因此我们采取以下方式:产生的随机数当作是索引,索引对应数据范围的一个数据,每次生成一个随机数就减少一个数据,并且下次随机数生成范围也相应减小,这样需要n个随机数时我们只需要生成n次即可。
如数据源是1 2 3 4 5 6 7 8 9 10 (基本数据集合)
第1次产生随机数范围是1到10,假设第1次产生的随机数是5,把5当作是索引,在基本数据集合中取第5个,并将5移除。
此时数据源变为 1 2 3 4 6 7 8 9 10
第2次产生随机数范围是1到9,假设第2次产生的随机数是8,把8当作是索引,在基本数据集合中取第8个,并将9移除
此时数据源变为 1 2 3 4 6 7 8 10
第3次产生随机数范围是1到8,假设第3次产生的随机数是6,把6当作是索引,在基本数据集合中取第6个,并将7移除
此时数据源变为 1 2 3 4 6 8 10
......
以此类推,直到选取的数据个数等于指定的个数。
注:生成的随机数小于等于基本数据集合的数据个数。
六、附-C语言实现
#include <stdio.h>
#include <stdlib.h>
#include <time.h> //随机生成用到time做种子
int INTVALID = -1;//当作是无效的
int* baseArrPtr = NULL;//基本数据数组
//从size大小的arrPtr集合中取第index个数据
int take(int* arrPtr, int size, int index)
{
int i, j;
if (NULL == baseArrPtr)
{
return -1;
}
for (i = 0, j = 0; i<size; i++)
{
if (arrPtr[i] != INTVALID)
{
if (j == index)
{
j = arrPtr[i];
arrPtr[i] = INTVALID;
return j;
}
j++;
}
}
return -1;
}
//从min到max范围内取cnt个数据并填充到arr
int getUniqueRandomArr(int arr[], int cnt, int min, int max)
{
int i, j;
int randNum;//随机数
int baseSize = max - min + 1;
int tmpSize = baseSize;
INTVALID = min - 1;
if (min >= max)
{
return -1;
}
if (cnt>baseSize)
{
return -2;
}
if (NULL != baseArrPtr)
{
free(baseArrPtr);
}
//为基本数据数组分配空间
baseArrPtr = (int*)malloc(baseSize*sizeof(int));
if (NULL == baseArrPtr)
{
return -3;
}
//初始化基本数据数组
for (i = 0, j = min; i < baseSize; i++, j++)
{
baseArrPtr[i] = j;
}
srand(time(0));//设置随机数种子
//rand()%num产生 0~num-1
//rand产生范围数公式rand()%(m+1-n)+n;有效范围在 [n,m]
for (i = 0; cnt>0; cnt--)
{
randNum = rand() % tmpSize;//randNum的有效范围在 [0,baseSize-1]
arr[i++] = take(baseArrPtr, baseSize, randNum);
tmpSize--;
}
free(baseArrPtr);
return i;
}
int main(int argc, char *argv[])
{
int i;
int min;
int max;
int cnt;
int arr[128] = { 0 };
printf("Please input min, max, count:");
scanf("%d %d %d", &min, &max, &cnt);
getUniqueRandomArr(arr, cnt, min, max);
for (i = 0; i<cnt; i++)
{
printf("%d ", arr[i]);
}
printf("\nFinish.\n");
//system("pause");
return 0;
}
效果: