所以我有一个<T,Integer>
类型的哈希映射。 T是通用类型。
T是任意对象,整数是我们当前在地图中拥有的对象的数量。
例如,字符串“shirt”并且具有映射到它的值为3.
我想构建一个名为random的方法,它将根据对象的当前分布从地图中返回一个随机对象。
意思是,如果我的地图中有2个键......“衬衫”和“裤子”。我的地图上有3个“衬衫”和7个“裤子”。分配应该是返回衬衫的30%的时间,并且70%的时间将返回“裤子”。
我如何使用随机生成器做这样的事情?
答案 0 :(得分:2)
该地图数据类型使您很难实现您的要求。更好的数据结构将使其变得更加容易。有各种可能的数据结构可以优化不同的东西。
如果您需要经常选择随机值,并且如果您没有太多不同的值/值计数,那么这个解决方案非常有效:
简单的List<T>
呢?
// Calculate this in advance
List<T> values =
map.entrySet()
.stream()
.flatMap(entry -> Stream.generate(() -> entry.getKey())
.limit(entry.getValue()))
.collect(Collectors.toList());
根据您的示例,此数组现在将包含3份"Shirts"
和7份"Pants"
现在很容易随机选择一个具有所需概率分布的值:
SecureRandom random = new SecureRandom();
T randomValue = values.get(random.nextInt(values.length));
证明:
Map<String, Integer> result = new HashMap<>();
for (int i = 0; i < 100000; i++)
result.compute(values.get(random.nextInt(values.length)),
(s, j) -> j == null ? 1 : j + 1);
System.out.println(result);
......收益率:
{Shirts=29955, Pants=70045}
答案 1 :(得分:0)
如果地图包含(虚拟)3件衬衫和7件裤子,则表示地图总共包含10个元素。
遇到衬衫的概率是3 /(7 + 3)= 0.3
遇到裤子的概率是7 /(7 + 3)= 0.7
现在,想象一下你可以掷骰子从0.0到1.0。如果骰子显示0.0到0.3之间的数字 - >挑一件衬衫。如果骰子显示0.3到1.0之间的数字 - >挑一条裤子。
以下代码实现了这个想法:
private static <T> T randomBasedOnOcurrenceDistribution(Map<T, Integer> map) {
SecureRandom random = new SecureRandom();
double total = map.values().stream().mapToInt(Integer::intValue).sum();
double dice = random.nextDouble();
double floor = 0.0;
for (Entry<T, Integer> entry : map.entrySet()) {
double currentProbability = entry.getValue() / total;
if(dice < floor + currentProbability) {
return entry.getKey();
}
floor += currentProbability;
}
throw new RuntimeException("Unreachable");
}
用法(复制自@Lukas答案):
Map<String, Integer> map = new HashMap<>();
map.put("Shirt", 3);
map.put("Pant", 7);
Map<String, Integer> result = new HashMap<>();
for (int i = 0; i < 100000; i++)
result.compute(randomBasedOnOcurrenceDistribution(map),
(s, j) -> j == null ? 1 : j + 1);
System.out.println(result); //prints {Pant=69679, Shirt=30321}