【问题标题】:How to get 2 random (different) elements from a c++ vector如何从 C++ 向量中获取 2 个随机(不同)元素
【发布时间】:2011-01-18 07:01:20
【问题描述】:

我想从 std::vector 中随机获取 2 个不同的元素。我怎样才能做到这一点:

  • 速度很快(在我的算法中完成了数千次)
  • 优雅
  • 元素选择确实是均匀分布的

【问题讨论】:

  • 小问题:您说您需要在算法中执行数千次...您希望每次都有一个不相关的结果还是不想看到您拥有的一对已经见过(例如,在抽样中它并没有真正的帮助)?
  • @Matthieu 每次都不相关。它用于马尔可夫链。

标签: c++ vector random


【解决方案1】:

为了优雅和简洁:

void Choose (const int size, int &first, int &second)
{
  // pick a random element
  first = rand () * size / MAX_RAND;
  // pick a random element from what's left (there is one fewer to choose from)...
  second = rand () * (size - 1) / MAX_RAND;
  // ...and adjust second choice to take into account the first choice
  if (second >= first)
  {
     ++second;
  }
}

使用 first 和 second 来索引向量。

为了统一,这是非常棘手的,因为随着大小接近 RAND_MAX,会偏向较低的值,如果大小超过 RAND_MAX,那么将有永远不会选择的元素。解决这个问题的一种方法是使用二分搜索:

int GetRand (int size)
{
  int lower = 0, upper = size;
  do
  {
    int mid = (lower + upper) / 2;

    if (rand () > RAND_MAX / 2) // not a great test, perhaps use parity of rand ()?
    {
       lower = mid;
    }
    else
    {
       upper = mid;
    }
  } while (upper != lower); // this is just to show the idea,
                            // need to cope with lower == mid and lower != upper
                            // and all the other edge conditions

  return lower;
}

【讨论】:

  • 简单而好的方法。也许还有另一个随机生成器,但我认为我的代码看起来像这样。这是保证停止(你永远不确定的无限while循环:)):)
  • 第一个代码 sn-p 不起作用:如果你得到second 的最后一个元素会发生什么?然后增加索引并溢出。把(size - 1)去掉就可以缓存size / MAX_RAND了。
  • @graham.reeds:从内存中,我假设 rand () 生成了一个介于 0 和 (RAND_MAX - 1) 之间的值,即它永远不会返回值 RAND_MAX。在检查文档时,它似乎生成了 0 到 RAND_MAX 范围内的数字。因此,只需将 size 替换为 (size - 1) 并将 (size - 1) 替换为 (size - 2)。但是算法是健全的,这很重要。
  • 我很难理解 GetRand 是如何统一的,除非有 2 次幂的可能情况(我懒得检查 size 是否是有效结果)。
  • @AProgrammer:你说得对,给定的代码可能仅适用于大小为 2 的幂的情况。您可以通过更改决策以考虑决策点两侧的数字数量来克服此问题: if (rand () * (left_size + right_size) / (left_size * RAND_MAX)
【解决方案2】:

您需要从 [0, N) 范围内生成 M 个均匀分布的随机数,但这里有一个警告。

需要注意的是,您对问题的陈述是模棱两可的。均匀分布的选择是什么意思?一件事是说必须以相等的概率(当然是 M/N)选择每个索引。另一件事是说必须以相等的概率选择每个双指数组合。这两个是不一样的。你想到了哪一个?

如果 M 远小于 N,则在 [0, N) 范围内选择 M 个数字的经典算法是 Bob Floyd 算法,该算法可在 Bentley 的“Programming Peals”一书中找到。它看起来如下(草图)

for (int j = N - M; i < N; ++j) {

  int rand = random(0, j); // generate a random integer in range [0, j]

  if (`rand` has not been generated before)
    output rand;
  else
    output j;
}

为了检查是否已经为相对较高的 M 生成了 rand,需要对集合进行某种实现,但在您的情况下,M=2 很简单。

请注意,此算法均匀分布 M 个数字的集合。此外,该算法需要精确的 M 次迭代(尝试)来生成 M 个随机数,即它不遵循在旨在解决同一问题的各种 ad-hoc 算法中经常使用的有缺陷的“试错”方法。

根据您的具体情况调整上述内容,正确的算法如下所示

first = random(0, N - 2);  
second = random(0, N - 1);
if (second == first)
  second = N - 1;

(我省略了random(a, b) 的内部细节作为实现细节)。

为什么上面的工作正确并产生真正均匀的分布可能不是很明显,但确实如此:)

【讨论】:

  • 请解决这个问题:for (int j = N - M; i
  • 如果试错法如此“有缺陷”,那么告诉我如何在没有这种“试错”的情况下在给定二进制随机源的情况下生成 [0, j] 范围内的统一整数首先方法? M 步只是一种错觉。
  • @user2345215:首先,在上述算法中,生成给定范围内的随机整数是一个低级问题。这使您的问题变得毫无意义。即使您的随机整数生成方法以某种方式需要反复试验迭代,这仍然不意味着可以在更高级别的算法中在其之上再添加一层反复试验迭代。其次,专门从“二进制随机源”构建整数的要求从何而来?
【解决方案3】:

如何使用std::queue 并对它们执行std::random_shuffle。那就尽情享受吧?

【讨论】:

  • 这是 O(N) 时间和 O(N) 空间。选择 2 个随机元素可以在 O(1) 时间和 O(1) 空间内完成。
  • 尽管如此简短,但这是(1)保证非无限运行时间和(2)不正确使用 rand() 的少数答案之一。不利的一面是 random_shuffle() 可能不快,正如提问者所要求的那样。
  • 但是单次洗牌会比所有 rand() 加在一起的速度慢吗?
  • 有时会,有时不会,因为迭代次数未知,因此这里的许多其他答案不符合提问者的标准。
【解决方案4】:

不优雅,但非常简单:只需在 [0, vector.size()[ 中绘制一个随机数并检查它是否相同。

简单在某种程度上也是优雅的;)

你叫什么快?我想这可以在一毫秒内完成数千次。

【讨论】:

  • 好的,所以你会使用 rand。那么问题是,如果我做 rand()%vector.size() 数字不是均匀分布的。
  • 如果问题是随机化,那么就使用好的一个:boost.org/doc/libs/1_42_0/libs/random/index.html
  • 彼得从不使用 rand() % vector.size()linux.die.net/man/3/rand 使用类似 rand() * vector.size() / RAND_MAX 的东西,这承诺是统一的。
  • @Artyom 你保证这永远不会溢出吗?顺便说一句,在您链接到的页面上,没有关于统一性的承诺。
  • rand() 是 LCG,对吧?可能想阅读维基百科页面上的缺点。分布似乎不均匀:en.wikipedia.org/wiki/…
【解决方案5】:

每当需要随机数时,您都会对随机数的均匀性、分布等属性提出各种问题。

假设您为您的应用程序找到了合适的随机源,那么生成不相关条目对的最简单方法就是选择两个随机索引并测试它们以确保它们不相等。

给定一个包含 N+1 个条目的向量,另一种选择是在 0..N 范围内生成索引 i。 element[i] 是选择之一。交换元素 i 和 N。在 0..(N-1) 范围内生成索引 j。 element[j] 是您的第二选择。这会慢慢地打乱你的向量,这可能是有问题的,但可以通过使用第二个向量来避免它,该向量将索引保存到第一个向量中,并对其进行改组。这种方法用交换来换取索引比较,并且对于小向量(通常是十几个或更少的元素)往往更有效,因为它避免了随着冲突数量的增加而不得不进行多重比较。

【讨论】:

    【解决方案6】:

    您可能想查看gnu scientific library。那里有一些非常好的随机数生成器,可以保证随机到比特级别。

    【讨论】:

      猜你喜欢
      • 2011-10-20
      • 1970-01-01
      • 2018-10-19
      • 1970-01-01
      • 2010-10-04
      • 2019-11-24
      • 2015-08-09
      • 2014-04-21
      • 1970-01-01
      相关资源
      最近更新 更多