关于随机数权重的实现

一、问题定义:

问下有一个数组,这些数组中的值都有自己的权重,怎样设计才能高效的优先取出权重高的数??

例如:
如                    权重: 8  2  11  79
        权重返回的值: 0  1  2   3

 

二、分析问题:

思路一:创建一个数组数组大小为权重和的大小,如值0的权重是8,则放入8个0值,值1的权重是2,则放入2个1值,依次类推。

                然后用用一个权重和大小的随机数,产生随机数,即可。缺点要占用过多的内存。

思路二:

权重和数组 w[i]存储的是[0,i]元素的所有元素的权重和  时间复杂度O(n) 空间复杂度O(n)
随机[0,W[399]] 看随机数 落在哪个Wi 内就选哪个  时间复杂度 O(longn) 
所以总的时间复杂度时间复杂度O(n) 空间复杂度O(n)

伪代码:

轮盘赌 并不是一种特别好的选择算子,但它容易实现。
首先要明白一点,由于交叉、变异等算子,并不能控制进化方向,所以进化的重任落在选择算子上。
如果明白了这一点,就好办了。

轮盘赌,就是积累概率来实现的,通常适应度大的被选择的几率较高。
假如:fit为适应度数组,共m个
for i=1 to m '先求和
sum=sum+fit(i)
next i
For i = 1 To n ‘n-是要生成多少个个体
temp = temp + fit(i)
If rnd <= temp / sum Then
   输出 i 就是结果
Exit Function
End If
Next i
 

三、解决问题:

思路二的代码:

 

package datastruct;

import java.util.HashMap;
import java.util.Map;

/**
权重随机数:
如              权重:8  2  11  79
        权重返回的值:0  1  2   3
@author ajian005 79331356@qq.com
2014-2-16 21:12
输出结果:{2.0=184128, 11.0=348551, 79.0=1308100, 8.0=159221}
*/

public class WeightRandomTest {
	private static double[] weightArrays = {8.0,2.0,11.0,79.0};  // 数组下标是要返回的值,数组值为数组下标的权重
	public static void main(String[] args) {
		WeightRandom weightRandom = new WeightRandom();
		Map<Double, Integer> stat = new HashMap<Double, Integer>();
		for (int i = 0; i < 2000000; i++) {
			int weightValue = weightRandom.getWeightRandom(weightArrays);
			if (weightValue < 0) {
				continue;
			}
			System.out.println("按权重返回的随机数:" + weightValue);
			if (stat.get(weightArrays[weightValue]) == null) {
				stat.put(weightArrays[weightValue], 1);
			} else {
				stat.put(weightArrays[weightValue], stat.get(weightArrays[weightValue])+1);
			}
		}
		System.out.println(stat);
	}
}

class WeightRandom {
	java.util.Random r = new java.util.Random();
	private double weightArraySum(double [] weightArrays) {
		double weightSum = 0;
		for (double weightValue : weightArrays) {
			weightSum += weightValue;
		}
		return weightSum;
	}
	public int getWeightRandom(double [] weightArrays) {
		double weightSum = weightArraySum(weightArrays);
		double stepWeightSum = 0;
        double random = Math.random();
		for (int i = 0; i < weightArrays.length; i++) {
			stepWeightSum += weightArrays[i];
			if (random <= stepWeightSum/weightSum) {
				//System.out.println(i);
				return i;
			}
		}
		System.out.println("出错误了");
		return -1;
	}	
}

 

 

 

四、归纳总结:

俄罗斯轮盘赌就是积累概率来实现

按权重负载调度等

 

五、参考资料:

关于随机数权重的交流:http://bbs.csdn.net/topics/390422881

能否说说遗传算法中轮盘赌选择方法的过程: http://wenwen.soso.com/z/q514113741.htm

<think>好的,用户问的是如何在Java中实现权重随机数算法。首先,我需要回忆一下相关的知识。带权重随机数,应该是根据不同的权重值,让每个选项有不同的被选中的概率。常见的方法有线性扫描、前缀和加二分查找,或者使用TreeMap这样的数据结构。 用户之前提供的引用里提到了TreeMap的实现,还有前缀和和二分查找的优化方法。比如引用[3]中提到的银行抽奖例子,使用TreeMap来存储累积权重,然后利用ceilingKey方法查找随机数对应的奖品。这可能是一个关键点。 首先,我应该考虑基本的实现方法。线性扫描虽然简单,但在选项很多时效率不高。比如,假设有三个选项,权重分别是1、2、3,总权重是6。生成一个0到6之间的随机数,然后依次减去每个权重,直到找到对应的区间。这种方法的时间复杂度是O(n),对于大量数据来说可能不够高效。 接下来是优化方法,比如前缀和数组配合二分查找。这种方法需要预先计算前缀和数组,然后生成随机数后,用二分查找找到对应的位置,这样时间复杂度可以降到O(log n)。这在引用[1]中也提到过,处理大量选项时应考虑这种优化。 另外,TreeMap的实现方式可能更高效,因为TreeMap的ceilingKey方法本身就是基于红黑树实现的,查找效率是O(log n)。这在引用[3]的例子中被使用,所以需要详细说明如何利用TreeMap来存储累积权重,并找到对应的键。 然后,我需要考虑如何将这些方法用Java代码实现。比如,线性扫描的具体步骤:计算总权重生成随机数,遍历每个元素减去权重直到找到负数。代码示例应该清晰,用户可以直接复制使用。 对于前缀和和二分查找的方法,需要先计算前缀和数组,然后使用Arrays.binarySearch方法。这里需要注意,如果随机数不在数组中,binarySearch会返回插入点,需要处理负数的情况。 TreeMap的方法则需要将累积权重作为key,对应的对象作为value,然后使用ceilingKey来找到最小的键大于等于随机数。这个方法可能代码更简洁,但需要理解TreeMap的特性。 还要考虑权重为0的情况,或者权重总和为0的情况,这时候可能需要异常处理。此外,权重是否可以是浮点数,或者必须是整数?根据引用中的例子,权重可以是double类型,所以代码中应该支持浮点数。 用户可能的应用场景,比如游戏抽奖、负载均衡、随机推荐等,需要简要提及。同时,性能优化的部分需要强调在不同数据量下的选择,比如小数据量用线性扫描,大数据量用二分或TreeMap。 最后,生成相关问题,可能涉及性能分析、优化方法、应用场景或者其他实现方式,比如随机池抽样等。需要确保问题与主题相关,帮助用户深入理解。</think>### Java实现权重随机数算法详解 #### 一、核心实现原理 带权重随机算法的核心是**概率空间映射**。假设有$n$个元素,每个元素权重为$w_i$,总权重$W=\sum_{w_i}$,算法通过以下步骤实现: 1. 构建累积概率分布 2. 生成[0,W)区间的随机数 3. 根据随机数定位对应元素 #### 二、基础线性扫描法 ```java public class WeightedRandom { private List<Double> weights = new ArrayList<>(); private double totalWeight; public void addItem(double weight) { weights.add(weight); totalWeight += weight; } public int nextIndex() { double random = Math.random() * totalWeight; for (int i = 0; i < weights.size(); i++) { random -= weights.get(i); if (random < 0) { return i; } } return weights.size() - 1; } } ``` 时间复杂度:$O(n)$,适用于小型数据集[^3] #### 三、优化方案:前缀和+二分查找 ```java public class OptimizedWeightedRandom { private double[] prefixSum; public OptimizedWeightedRandom(List<Double> weights) { prefixSum = new double[weights.size()]; double sum = 0; for (int i = 0; i < weights.size(); i++) { sum += weights.get(i); prefixSum[i] = sum; } } public int nextIndex() { double random = Math.random() * prefixSum[prefixSum.length-1]; int index = Arrays.binarySearch(prefixSum, random); return Math.abs(index + 1); } } ``` 时间复杂度:$O(\log n)$,适合大规模数据[^1] #### 四、TreeMap实现方案 ```java public class TreeMapWeightedRandom { private TreeMap<Double, Integer> map = new TreeMap<>(); private double totalWeight; public void addItem(int index, double weight) { totalWeight += weight; map.put(totalWeight, index); } public int nextIndex() { double random = Math.random() * totalWeight; return map.ceilingEntry(random).getValue(); } } ``` 利用红黑树特性实现$O(\log n)$查询效率[^3] #### 五、应用场景 1. 游戏抽奖系统(不同概率的奖品掉落) 2. 负载均衡(带权重的服务节点选择) 3. 推荐系统(内容按权重曝光) #### 六、性能对比 | 方法 | 初始化耗时 | 查询耗时 | 适用场景 | |-----------------|------------|----------|-----------------| | 线性扫描 | $O(1)$ | $O(n)$ | 小数据集 | | 前缀和+二分查找 | $O(n)$ | $O(\log n)$ | 静态权重 | | TreeMap | $O(n\log n)$ | $O(\log n)$ | 动态更新权重 | #### 七、注意事项 1. 权重值必须为非负数 2. 总权重不能为0 3. 浮点数精度问题(建议使用BigDecimal处理货币等场景)
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值