为什么C++14中std::shuffle方法被弃用?

57

根据cppreference.com关于std::shufle的参考文献,以下方法在c++14中已被弃用:

template< class RandomIt >
void random_shuffle( RandomIt first, RandomIt last );

为什么我们将不能再不传第三个参数来调用以下函数?

std::random_shuffle(v.begin(),v.end()); //no longer valid in c++14

看起来不像是另一个函数声明有默认参数集。这背后的原因是什么?是否添加了某种替代方案?

5个回答

53

std::random_shuffle在内部可能会使用C系列的random函数。这些函数使用全局状态来保存种子和其他状态。

因此,它正在被弃用,因为shuffle会做相同的事情,但更好。也就是说,它使用C++11中的新<random>头文件,该文件不使用全局状态,而是使用生成器、设备和分布来创建自己的对象。


12
std::random_shuffle可能会使用rand。但它并不强制要求使用rand。一些实现会使用,而另一些则不会。 - Howard Hinnant
感谢您的纠正,我在那里犯了错误。对于这个不准确之处,我很抱歉。 - Germán Diago

47

std::random_shuffle (实际上)被std::shuffle替代。你需要传递第三个参数(一个随机数生成器),但是作为交换,你会获得更好的定义和(通常)行为。

std::random_shuffle 定义相当不好。它通常使用rand()生成随机数,但没有任何说明它是否(以及如何)调用srand,因此你无法依赖于rand在你想要的方式下种子。(如果你种子了rand,你也不能保证它被使用)。如果我没记错的话,还有一些令人困惑(有点自相矛盾)的语言,可以解释为random_shuffle根本无法使用rand,或者它不能用srand进行种子。即使在最好的情况下,许多rand()实现都相当差劲,因此即使在最好的情况下,你也不能依赖于有用的结果。

最后结论:没有损失,使用std::shuffle代替random_shuffle,你的代码会更好。


似乎有一个接受 RNG 的第二个重载……那个可用吗? - user541686
@Mehrdad:我认为是的。它基本上等同于 std::shuffle。但我并没有看到任何真正理由优先选择它而不是 std::shuffle - Jerry Coffin
14
random_shuffle中的RandomNumberGeneratorshuffle中的UniformRandomNumberGenerator具有不同的API。后者的API符合[rand.req.urng]中的均匀随机数生成器要求。前者的API仅在random_shuffle的要求子句中指定。 - Howard Hinnant

16
我们可以在此文档中找到理由:N3775: Deprecating rand and Friends,它说:
因此,我们现在建议执行下一步计划,以阻止使用传统的 C 函数 rand,以及与其相关的 seeding 函数 srand 和上限宏 RAND_MAX。6 特别是,我们建议通过正式弃用以下内容来开始这个过渡:
- rand、srand 和 RAND_MAX - algorithm random_shuffle() (保留 shuffle)。
弃用 random_shuffle() 的原因是一个重载被指定为依赖于 rand,而另一个重载被指定为需要一个难以生成的分布对象;这样的分布已经是我们保留的 shuffle 的一个隐含部分。
还有一份后续文档Discouraging rand() in C++14, v2 重新阐述了这个立场。
更新
正如 Howard Hinnant 指出的那样,《N3775》存在错误:rand_shuffle() 允许但不要求在内部使用 rand(),但这不会改变理由。

2
N3775在错误地说rand_shuffle()被指定为依赖于rand。正确的说法是它允许在rand的基础上实现。但并不要求在rand的基础上实现。 - Howard Hinnant
@HowardHinnant 感谢您指出这一点,我一直在想为什么cppreference使用了那种措辞,我应该自己查看标准。 - Shafik Yaghmour

6

random_shuffle被弃用,因为它的RNG未指定——不仅是你不必指定,而且标准本身也没有指定。例如在VC++中,它使用实现非常差的rand()


4
其他回答已经回答了这个问题,但如果有人正在寻找一些适合您需求的剪贴代码,以下可能会有所帮助:
#include <random>
...
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(v.begin(), v.end(), g);

rd 在许多平台上产生非确定性的随机数据,生成器 g 使用具有大状态的 Mersenne Twister 伪随机数生成器


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接