27、随机算法
1、随机算法的简单分类
2、最基础的公平随机取样
public class Pick {
private final Map<Integer, ArrayList<Integer>> map;
private final Random random;
public Pick(int[] nums) {
random = new Random();
map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int key = nums[i];
if (!map.containsKey(key)) map.put(key, new ArrayList<>());
map.get(key).add(i);
}
}
public int pick(int target) {
ArrayList<Integer> list = map.get(target);
int size = list.size();
// return list.get(random.nextInt(size));
return list.get((int) (random.nextDouble() * size));
}
}
3、加权随机取样
public class PickIndex {
private final int[] presum;
private final int sum;
private final Random random;
public PickIndex(int[] w) {
// w[i] 代表第 i 个任务的权重, 也就是说, 应该给第 i 个任务发放 w[i] 张彩票
// 给每个任务发放彩票, 总共发了 sum(w[]) 张彩票
// 我们只需要记录每个任务的彩票区间就好了, 更近一步, 只需要记录每个任务第一张彩票的编号
// 因为下一个任务第一张彩票的编号 = 上一个任务最后一张彩票的编号 + 1
// presum[i] = 第 i 个任务第一张彩票的编号
presum = new int[w.length];
for (int i = 1; i < presum.length; i++) presum[i] = presum[i - 1] + w[i - 1];
sum = presum[w.length - 1] + w[w.length - 1];
random = new Random();
}
public int pickIndex() {
int x = random.nextInt(sum);
// 二分查找 presum[i] <= x 的最大值对应的 i
int l = 0;
int r = presum.length - 1;
int mid;
while (l < r) {
mid = l + (r - l + 1) / 2;
if (presum[mid] <= x) l = mid;
else r = mid - 1;
}
return l;
}
}
4、生成任意区间随机数
// 作业
5、拒绝采样
6、使用 rand7,实现 rand10
public class Rand10 {
/**
* 1 ~ 7
*/
public int rand7() {
return new Random().nextInt(7) + 1;
}
/**
* 1 ~ 10
*/
public int rand10() {
while (true) {
int a = rand7() - 1; // a in 0 ~ 6
int b = rand7() - 1; // b in 0 ~ 6
int t = a * 7 + b; // t in 0 ~ 48
if (t >= 40) continue;
return t % 10 + 1;
}
}
}
// 作业
7、多次采样和洗牌算法
8、KnuthShuffle
public class KnuthShuffle {
private final int[] nums;
private final Random random;
public KnuthShuffle(int[] nums) {
this.nums = nums.clone();
random = new Random();
}
public int[] reset() {
return nums.clone();
}
/**
* 从后向前决定排列中的每个元素
*/
public int[] shuffle1() {
int[] data = nums.clone();
for (int i = data.length - 1; i >= 0; i--) {
int j = random.nextInt(i + 1);
swap(data, i, j);
}
return data;
}
/**
* 从前向后决定排列中的每个元素
*/
public int[] shuffle2() {
int[] data = nums.clone();
for (int i = 0; i < data.length; i++) {
// random[i ... data.length - 1] = random[0 ... data.length - 1 - i] + i
int j = random.nextInt(data.length - i) + i;
swap(data, i, j);
}
return data;
}
private void swap(int[] arr, int a, int b) {
int k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}
9、蓄水池抽样
// 这个题目的接口不是很好
// Solution(int k)
// void fetch(int newValue)
// int[] sampling()
public class GetRandom {
private final ListNode head;
private final Random random;
public GetRandom(ListNode head) {
this.head = head;
random = new Random();
}
/**
* 蓄水池抽样, 蓄水池的大小为 1
*/
public int getRandom() {
int res = head.val;
int index = 1;
for (ListNode cur = head.next; cur != null; cur = cur.next, index++) {
int j = random.nextInt(index + 1);
if (j == 0) res = cur.val;
}
return res;
}
}
10、随机采样算法小节
本文来自博客园,作者:lidongdongdong~,转载请注明原文链接:https://www.cnblogs.com/lidong422339/p/17333662.html