您的位置:首页 > 其它

采样问题

2015-04-08 11:06 169 查看

问题描述

从0~n-1中随机选取m(m < n)个不同整数,且使m个整数有序输出

已知条件

bigRand()产生一个远大于m和n的正整数;

randInt(int i,int j)产生一个[i,j]的随机数;

sort(int []a,int lo,int hi)对a中指定范围内数据排序

解决方法

Knuth的S算法】从r个剩余元素中选择s个元素,以s/r的概率选择下一个。

/**
* 从0~n-1中随机选取m个随机数,且m个随机数输出结果有序。
* 时间复杂度:O(n);空间开销:O(1)
* 应用场景:n比较小
* @param m
* @param n
*
*/
public static void genKnuthS(int m,int n)
{
int select=m;
int remaining=n;
for(int i=0;i<n;i++)
{
//从r个剩余的整数中选择s个,以s/r的概率选择下一个
if((bigRand()%remaining)<select)
{
System.out.println(i);
select--;
}
remaining--;
}
}


随机整数插入有序集合,丢掉大量重复随机数

/**
* 随机整数插入有序集合
* 时间复杂度:O(mlogm);空间开销:集合存储开销。
* 应用场景:适合m相对n较小情况
* @param m
* @param n
*/
public static void genSets(int m,int n)
{
//使用Integer包装类,不如C++效率高
Set<Integer>S=new TreeSet<>();

//m个不同随机数插入到集合中,丢掉重复的随机数
while(S.size()<m)
S.add(bigRand()%n);

//遍历集合,随机数有序输出
for(int i:S)
System.out.println(i);

}


【Floyd算法】随机整数插入有序集合,如果出现重复随机数,用本轮最大随机数代替,保证只选取m次

/**
* 随机整数插入有序集合
* 时间复杂度:O(mlogm);空间开销:集合存储开销
* 应用场景:m相对与n较小(最坏情况也只进行m次选取)
* @param m
* @param n
*/
public static void genFloyd(int m,int n)
{
Set<Integer> S=new TreeSet<>();
int t;
for(int i=n-m;i<n;i++)
{
t=bigRand()%(i+1);
if(!S.contains(t))
S.add(t);
else S.add(i);//出现重复,选择本轮最大随机数,i一定不再集合中
}
//遍历集合,随机数有序输出
for(int i:S)
System.out.println(i);
}


【Knuth P算法】打乱包含0~n-1个整数的数组,前m个元素排序输出。不足:可能存在重复的随机数。

/**
* 打乱包含0~n-1个整数的数组,前m个元素排序输出
* 时间复杂度:O(n+mlogm);空间复杂度:O(n)
* @param m
* @param n
*/
public static void genKnuthP(int m,int n)
{
int []a=new int
;
for(int i=0;i<n;i++)
a[i]=i;
//对n个元素进行洗牌
for(int j=0;j<n;j++)
{
//交换a[j]、a[rand]
int rand=randInt(j,n-1);
int t=a[j];
a[j]=a[rand];
a[rand]=t;
}
//前m个元素排序输出,其中可能存在重复元素
sort(a,0,m-1);
for(int k=0;k<m;k++)
System.out.println(a[k]);

}


【Knuth P’算法】只对前m个元素进行洗牌,并排序输出。

/**
* 打乱数组中前m个元素,并排序输出
* @param m
* @param n
*/
public static void genShuf(int m,int n)
{
int []a=new int
;
for(int i=0;i<n;i++)
a[i]=i;
//前面个元素进行洗牌
for(int j=0;j<m;j++)
{
//a[j]<-rand、a[rand]<-j
int rand=randInt(j,n-1);
a[j]=rand;
a[rand]=j;
}

//前m个元素排序输出,其中可能存在重复元素
sort(a,0,m-1);
for(int k=0;k<m;k++)
System.out.println(a[k]);

}


补充

bigRand()的一种实现

/**
* 产生很大的随机整数
* @return
*/
private static int bigRand()
{
return (int)(Integer.MAX_VALUE*r.nextDouble()+r.nextDouble());//r为Random类的一个实例对象;
}


randInt(int i,int j)的一种实现

/**
* 返回[i..j]范围内均匀选择的随机整数
* @param i
* @param j
* @return
*/
private static int randInt(int i,int j)
{
return i+(int)(r.nextDouble()*(j-i+1));
//return i+bigRand()%(j-i+1);
}


sort(int []a,int lo,int high) 一种快排的实现

/**
* 对数组下标lo...hi之间的元素进行快速排序
* @param a  待排序数组
* @param lo 最低位
* @param hi 最高位
*/
public static void sort(int[] a,int lo,int hi){
if(hi<=lo)return;
int j=partition(a,lo,hi);
sort(a,lo,j-1);
sort(a,j+1,hi);
}
/**
* 一次划分
* @param a  待排序数组
* @param lo 最低位
* @param hi 最高位
* @return    划分点
*/
private static int partition(int []a,int lo,int hi){
int i=lo,j=hi+1;
int v=a[lo];    //最低位的元素作为划分点
while(true){
while(less(a[++i],v)) if(i==hi) break;
while(less(v,a[--j])) if(j==lo) break;
if(i>=j)break;
exch(a,i,j);
}
exch(a,lo,j);
return j;
}


总结

一般来说采用knuth 的S或Floyd算法可以很好的解决这类有限集合非空真子集的选取问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息