开发一个加权随机算法:
核心主要有两点,一个是概率补偿,一个是即时更新权重占比
public static <T> Set<T> weightRandomSelect(Map<T, Double> weightMap, int k) {
double weightSum = 0;
Set<T> result = new HashSet<>();
for (Map.Entry<T, Double> entry : weightMap.entrySet()) {
weightSum += entry.getValue();
}
int size = weightMap.size();
if (size == 0) {
return result;
}
double averageWeight;
Iterator<Map.Entry<T, Double>> iterator = weightMap.entrySet().iterator();
int cursor = 0;
while (iterator.hasNext()) {
averageWeight = weightSum / size;
if (averageWeight == 0) {
result.addAll(randomSelect(iterator, cursor, weightMap.size(), k));
break;
}
Map.Entry<T, Double> entry = iterator.next();
cursor++;
if (k >= size) {
result.add(entry.getKey());
k--;
size--;
weightSum -= entry.getValue();
continue;
}
double value = entry.getValue();
if (value <= 0) {
size--;
weightSum -= entry.getValue();
continue;
}
double p = value / averageWeight;
if (isSelectAndWeight(size, k, p)) {
result.add(entry.getKey());
k--;
}
size--;
weightSum -= entry.getValue();
}
return result;
}
public static <T> Set<T> randomSelect(Iterator<Map.Entry<T, Double>> iterator, int traversedCount, int size, int k) {
int count = size - traversedCount;
Set<T> result = new HashSet<>();
if (count <= 0) {
return result;
}
while (iterator.hasNext()) {
Map.Entry<T, Double> next = iterator.next();
if (isSelect(count, k)) {
result.add(next.getKey());
k--;
}
count--;
}
return result;
}
private static boolean isSelect(int size, int needNum) {
if (size <= needNum) {
return true;
}
return RandomUtil.generateRandomNum(0, size) < needNum;
}
private static boolean isSelectAndWeight(int size, int needNum, double p) {
if (size <= needNum) {
return true;
}
return RandomUtil.generateRandomDouble(0, size) / p < needNum;
}