【问题标题】:Get N samples given iterator给定迭代器获取 N 个样本
【发布时间】:2013-05-15 07:57:32
【问题描述】:

给定一个迭代器 it 对数据点、我们拥有的数据点数量 n 以及我们想要用于进行某些计算的最大样本数 (maxSamples)。

想象一个函数calculateStatistics(Iterator it, int n, int maxSamples)。该函数应该使用迭代器来检索数据并对检索到的数据元素进行一些(大量)计算。

  • 如果n <= maxSamples 我们当然会使用从迭代器中获得的每个元素
  • 如果n > maxSamples,我们将不得不选择要查看哪些元素以及要跳过哪些元素

我在这方面花费了相当长的时间。问题当然是如何选择何时跳过元素以及何时保留它。到目前为止我的方法:

  • 我不想获取来自迭代器的第一个maxSamples,因为这些值可能不会均匀分布。
  • 另一个想法是使用随机数生成器,让我在0n 之间创建maxSamples(不同的)随机数并获取这些位置的元素。但如果例如n = 101maxSamples = 100 越来越难以找到尚未在列表中的新的不同数字,仅在随机数生成中就浪费了大量时间
  • 我最后的想法是反其道而行之:生成n - maxSamples 随机数并排除这些位置元素处的数据元素。但这似乎也不是一个很好的解决方案。

你对这个问题有什么好主意吗?可能有标准的已知算法吗?

【问题讨论】:

  • no maxSample 只是我们想要查看多少样本来进行计算的限制
  • 嗯,也许我不清楚。你说你不想拿第一个 maxSamples。所以我的问题是你需要随机抽取样本(正如你尝试的那样)还是你可以定期跳过一些样本(例如n=13max=9 所以你跳过 3、6、9 和 12 th示例)?
  • @TonyMorris 对不起,我误会你了。跳过一些样本也是可能的,是的。但是如何选择跳号呢?
  • @ChrisCM 我认为这是一个堆栈溢出问题,但当然不是一个简单的问题!我提出的 3 个解决方案都是不可接受的(否则我不会问),我想我在每个解决方案中都说明了他们的问题是什么。是的,正如您所说:“您可以提出的任何收藏都是可以接受的”,但问题是如何获得该收藏!
  • 如果“任何你能想出的集合”是答案,为什么不是前几个?因此很简单,否则你想要真正的伪随机性,这就是我发布答案的原因。尽管您缺乏随机访问权限,但这是唯一的方法。当您实施“跳过”类型的场景时,您注定会多次“接受其余部分”或“忽略其余部分”,这并不比只接受第一批更好。因此,迭代,创建一个临时随机访问向量,并从中随机选择是唯一剩下的答案,因此,我发布的答案。

标签: algorithm sampling


【解决方案1】:

为了提供一些答案,以下是在给定集合大小 > 元素的情况下收集一组随机数的好方法。 (在 C++ ish 伪代码中)。

编辑:您可能需要先迭代并创建“someElements”向量。如果您的元素很大,它们可以作为指向这些元素的“指针”以节省空间。

vector randomCollectionFromVector(someElements, numElementsToGrab) {
    while(numElementsToGrab--) {
         randPosition = rand() % someElements.size();
         resultVector.push(someElements.get(randPosition))
         someElements.remove(randPosition);
    }
    return resultVector;
}

如果您不关心更改元素向量,您还可以从 someElements 中删除随机元素,正如您所提到的。该算法看起来非常相似,同样,这在概念上是相同的想法,您只需通过引用传递 someElements 并对其进行操作。

值得注意的是,伪随机分布的质量,就它们的随机程度而言,会随着您使用的分布大小的增加而增长。因此,如果您根据导致使用更多随机数的方法来选择使用哪种方法,您可能会获得更好的结果。示例:如果您有 100 个值并且需要 99,您可能应该选择 99 个值,因为这将导致您使用 99 个伪随机数,而不仅仅是 1。相反,如果您有 1000 个值并且需要 99,您应该可能更喜欢删除 901 值的版本,因为您使用了更多来自伪随机分布的数字。如果您想要的是可靠的随机分布,这是一个非常简单的优化,它将大大提高您看到的“假随机性”的质量。或者,如果性能比分配更重要,您将采取替代方案,甚至只采用前 99 个值的方法。

【讨论】:

  • 不幸的是,随机访问是不可能的。我们只有一个迭代器。
  • 是的,因此是伪代码。您不必实际访问这些值,您可以轻松地拥有一些临时对象,然后简单地将此算法视为生成您想要访问的值表的一种方式,然后您可以对其进行迭代并获取。
  • 好的!但是,老实说(建议 2),我看不出你的回答与我上面的建议有何不同,除了我的回答是更少的伪代码和更具描述性?
  • 顺便说一句,您在最后一段中的讨论也是我的想法(再次参见建议 2 和 3)。然而,与你写的相反——这就是我问题的重点——我认为这不是一个好主意,例如在需要 99 的 100 个值分别选择 99 个值的情况下(如果它带有迭代器,您将存储然后只选择该元素,对吗?),但我宁愿选择选择 1 个元素的解决方案抛出离开。原因:选择了第 98 个元素后,第 99 个的概率是 1% - 你要尝试多少次才能得到一个尚未选择的元素?
  • 随机数问题的两个答案:A:避免已经选择的值的随机数不是随机的...... B:这就是为什么你取指针的临时向量,并在你删除它们时删除它们去。这仅取决于您想要哪种行为。请注意,当您从 someElements 中删除值时,您会根据其大小进行修改,该大小正在缩小。这会处理您的“抓取”重复项和“困难的随机数”生成。
【解决方案2】:
interval = n/(n-maxSamples) //an euclidian division of course
offset = random(0..(n-1)) //a random number between 0 and n-1
totalSkip = 0
indexSample = 0;
FOR it IN samples DO
    indexSample++ // goes from 1 to n
    IF totalSkip < (n-maxSamples) AND indexSample+offset % interval == 0 THEN
        //do nothing with this sample
        totalSkip++
    ELSE
        //work with this sample
    ENDIF
ENDFOR
ASSERT(totalSkip == n-maxSamples) //to be sure

interval 表示要跳过的两个样本之间的距离。 offset 不是强制性的,但它允许有很少的多样性。

【讨论】:

  • 在相同的预定间隔收集值并不比取第一组更好。
  • 嗯,我会说按照预定的时间间隔去是可以的。好吧,它并不是真正随机分布的,但我会说你比只拿第一批更好(知道我们正在实施的应用程序;-))。
  • @ChrisCM 我知道以相同的预定间隔收集值似乎与取第一束相似,但作为问题下的 cmets(以及此答案的 OP 的评论)建议关于 OP 的意愿更容易接受。
【解决方案3】:

根据讨论和对您问题的更深入了解,我提出以下建议。您可以利用质数的属性,我认为这将为您提供一个非常好的解决方案,它似乎会抓取伪随机数。如下代码所示。

#include <iostream>
using namespace std;


int main() {
    const int SOME_LARGE_PRIME = 577;  //This prime should be larger than the size of your data set.  
    const int NUM_ELEMENTS = 100;
    int lastValue = 0;
    for(int i = 0; i < NUM_ELEMENTS; i++) {
        lastValue += SOME_LARGE_PRIME;
        cout << lastValue % NUM_ELEMENTS << endl;
    }
}

使用此处介绍的逻辑,您可以创建一个包含从 1 到“NUM_ELEMENTS”的所有值的表。由于素数的属性,在您一直旋转回到数据集的大小之前,您不会得到任何重复。如果你然后取其中的第一个“NUM_SAMPLES”,并对它们进行排序,你可以遍历你的数据结构,并获取数字的伪随机分布(不是很好的随机,但比预先确定的间隔更随机),没有额外的空间,只有一次通过您的数据。更好的是,您可以通过每次抓取一个随机质数来更改分布的布局,再次必须大于您的数据集,否则下面的示例会中断。

PRIME = 3,数据集大小 = 99。不起作用。

当然,最终这与预先确定的间隔非常相似,但它插入了一定程度的随机性,而这是通过简单地抓取每个“size/num_samples”个元素无法获得的。

【讨论】:

    【解决方案4】:

    这称为水库采样

    【讨论】:

      猜你喜欢
      • 2017-11-26
      • 2012-09-25
      • 2016-07-24
      • 1970-01-01
      • 2017-10-23
      • 2014-07-19
      • 2012-02-17
      • 1970-01-01
      相关资源
      最近更新 更多