背包可以存储一组数据,随机背包要求每次迭代,所有N!种排列出现的概率相等。
方法:
用数组保存一组元素,并在迭代器中的构造函数中随机打乱他们的顺序:
此时的打乱并非将原数组中的元素重新排列,而是使用一个新数组存储打乱的下标,并用新数组元素的顺序去遍历原数组
支持如下API
1.RandomBag()
2.isEmpty()
3.size()
4.add(Item item)
代码
import edu.princeton.cs.algs4.StdRandom;
import java.util.Iterator;
/**
* @author 鯉伴MAY
* @param <Item>
*/
public class RandomBag<Item> implements Iterable<Item> {
private Item[] array;
private int N;
public RandomBag(){
array = (Item[]) new Object[1];
}
private void resize(int max){
Item[] temp = (Item[]) new Object[max];
for (int i = 0; i < N; i++) {
temp[i] = array[i];
}
array = temp;
}
public void add(Item item) {
if(N == array.length)
resize(2*array.length);
array[N++] = item;
}
public boolean isEmpty() {
return N==0;
}
public int size(){
return N;
}
@Override
public Iterator<Item> iterator() {
return new BagIterator();
}
private class BagIterator implements Iterator {
//使用一个数组记录打乱的下标,以打乱的下标去访问原数组,实现快速随机访问
private int[] seq = new int[N];
private int index = 0;
public BagIterator() {
for(int i=0;i<N;i++) {
seq[i] =i;
}
StdRandom.shuffle(seq);
/*
此函数的源码如下:
public static void shuffle(int[] a) {
validateNotNull(a);
int n = a.length;
//此函数遍历数组,使每一个元素都随机和另一个交换为位置
//此方法何以保证每个新的迭代器得到N!种排列出现的可能性相等
for (int i = 0; i < n; i++) {
int r = i + uniform(n-i); // between i and n-1
int temp = a[i];
a[i] = a[r];
a[r] = temp;
}
}
//返回0-n之间的随机一个正整数
public static int uniform(int n) {
if (n <= 0) throw new IllegalArgumentException("argument must be positive: " + n);
return random.nextInt(n);
}
*/
}
@Override
public boolean hasNext() {
return index<N;
}
@Override
public Object next() {
return array[seq[index++]];
}
}
}