【发布时间】:2015-07-23 11:35:01
【问题描述】:
我正在尝试使用 java 实现水库采样算法。我有 N 个大小未知的数据流(来自到达接收器节点的传感器的读数)。为了简单起见,假设我有一个未知大小的流。
因此,水库采样算法之一的建议是创建大小为reservoirSize 的水库。假设它是 5。您获得的前五个读数,将它们存储在您的水库中。好的。现在,随着您获得越来越多的读数,每次读数都会生成一个从 0 到读数的随机数,如果该随机数小于水库大小,则将读数存储在水库[randomNumber] 中。
所以可以说我的水库大小 = 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.5 秒的采样率,在我开始采样后一小时(这意味着已将大约 7000 个读数转发到汇节点),再过半小时即不会检测到测量量的变化,即读数表示更改将从存储库中丢弃。
算法实现
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 实现
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);
}
}
}
这是算法的正常行为还是我在这里遗漏了什么?如果这是正常行为,有没有办法让它更“准确”地工作?
【问题讨论】:
-
你的问题是什么?水库采样背后的想法是,在每个时间点,从 0 到现在的每次观察都有(有)相同的机会进入样本水库。 (如果您的应用程序需要运行平均值,我怀疑,请使用运行平均值)
-
我的问题是,在某个点之后我很少采样数据。例如,我在读数 17.098 处采集一个样本,而我的下一个读数是读数 23.491。这意味着我丢弃了超过 6.000 个读数。这是一个很小的比率,我丢失了很多信息。也许你是对的。也许水库采样不是我真正需要的。
标签: algorithm random reservoir-sampling