我正在尝试使用java实现油藏采样算法。我有大量未知数据的N个数据流(来自传感器的读数到达汇聚节点)。为简单起见,假设我有一个未知大小的流。
因此,水库采样算法建议的是建立一个规模为ReservoirSize的储层。让我们说它是5.你得到的前五个读数,将它们存储在你的水库中。好。现在,当您获得越来越多的读数时,每次读数都会生成一个从0到读数的随机数,如果该随机数小于reservSize,则将读数存储在水库中[randomNumber]。
所以,让我说我有ReservSize = 5,我刚拿到了我的第10个读数。我将生成一个从0到10的随机数,如果该数字小于5,我将把读数存储在随机数指向的位置。让我们说随机数是3,所以我在储藏库中存储读数10 [3]。
public void sample (Vector pool, double Measurement, int streamIndex) {
if (streamIndex < ReservoirSize){
pool.addElement(Double.toString(Measurement));
}
else if ((randomIndex=(int)ranNum.nextInt((streamIndex+1)))<ReservoirSize) {
pool.setElementAt(Double.toString(Measurement), randomIndex);
}
}
此代码的问题在于,一旦streamIndex变得足够大(例如,高于4.000),我很少会对任何读数进行采样。并且它确实有意义,因为从0到4000生成小于5的随机数的可能性明显小于从0生成随机数的可比性,比如100,即小于5.
我还从Vitters纸上实施了AlgorthmR,这里描述了另一种方式:
Gregable ReservoirSampling
但所有实现都有同样的问题。流越大,采样频率变得越小。因此,对于0.5s的采样率,在我开始采样后一小时(这意味着大约7000个读数已被转发到汇聚节点),测量数量的变化将在另一个良好的半小时内无法检测到,即读数表明变化将从水库中丢弃。
AlgorthmR实施
public RSAlgorithmR() {
this.currentPool = null;
this.randomStoreatIndex = 0;
this.randomIndex = 0;
this.ranNum = new Random();
}
public void sample (LLNode cNode, double Measurement) {
int streamIndex = cNode.getStreamIndex();
int storeatIndex =cNode.getStoreatIndex();
if (streamIndex < ReservoirSize) {
cNode.data.addElement(Double.toString(Measurement));
if (streamIndex == ( ReservoirSize - 1) ) {
randomStoreatIndex = (int)ranNum.nextInt(ReservoirSize);
cNode.setStoreatIndex((int)randomStoreatIndex);
}
}
else {
if (storeatIndex == streamIndex) {
randomIndex=(int)ranNum.nextInt(ReservoirSize);
cNode.data.setElementAt(Double.toString(Measurement), randomIndex);
randomStoreatIndex = (int)ranNum.nextInt(streamIndex - ReservoirSize) + ReservoirSize;
cNode.setStoreatIndex(randomStoreatIndex);
System.out.println("Index:: "+streamIndex);
System.out.println("randomIndex:: " + randomIndex);
}
}
cNode.setStreamIndex();
};
Gregable Implementation
public ReservoirSampler() {
this.currentPool = null;
this.randomIndex = 0;
this.ranProp = new Random();
this.ranInd = new Random();
}
public void sample (LLNode currentSpot, double humidityRead,
double temperatureRead, int streamIndex) {
double acceptancePropability = (double)ReservoirSize/streamIndex;
if (streamIndex < ReservoirSize){
currentSpot.humidityData.addElement(Double.toString(humidityRead));
currentSpot.temperatureData.addElement(Double.toString(temperatureRead));
}
else {
ranProp.setSeed(System.currentTimeMillis());
randomPropability=(double)ranProp.nextDouble();
if ( randomPropability < acceptancePropability){
ranInd.setSeed(System.currentTimeMillis());
randomIndex=(int)ranInd.nextInt((ReservoirSize));
currentSpot.humidityData.setElementAt(Double.toString(humidityRead),randomIndex);
currentSpot.temperatureData.setElementAt(Double.toString(temperatureRead),randomIndex);
}
}
}
这是algorthm的正常行为还是我错过了什么?如果这是正常行为,是否有办法让它更“准确”地运作?
答案 0 :(得分:2)
这是算法R的正常行为(参见Knuth&#34;计算机编程艺术&#34; 3.4.2)
但是,有更好的算法可供选择:
与算法R相比,这些算法在每个阶段绘制要跳过的流元素的数量,因此生成的随机数要少得多,特别是对于长流。
Re&#34;准确度&#34;:在所有算法(R,X,Y,Z,K,L,M)中,输入流中的每个元素同样可能在样本中。这可以通过数学证明并通过在相同输入流上运行相同算法多次并且测量每个元素被采样的频率来凭经验证明(您必须使用良好的PRNG,例如Mersenne Twister)。算法之间的主要区别在于生成的随机数量。
所有算法实现和测试都相对简单。算法L虽然不是最有效的算法,但它实现起来特别紧凑和简单,并且比算法R更有效。