如何确保两个不同的向量在C ++中以相同的顺序进行混洗?

时间:2013-06-06 16:53:12

标签: c++ random vector

我有两个向量:

  

vector1 = [1 2 3 4 5 6 7 8 9]

     

vector2 = [1 2 3 4 5 6 7 8 9]

我想确保,当我使用 random_shuffle 进行随机播放时,它们应该以相同的顺序进行混洗。例如:

改组后的输出应该是:

  

vector1 = [1 9 3 4 2 7 8 5 6]

     

vector2 = [1 9 3 4 2 7 8 5 6]

但我得到的输出如下:

  

vector1 = [5 1 7 4 2 3 9 8 6]

     

vector2 = [3 4 1 9 8 2 5 7 6]

继承我的代码:

int main () 
{
  std::srand ( unsigned ( std::time(0) ) );
  std::vector<int> vector1, vector2;

  // set some values:
  for (int i=1; i<10; ++i)
  {
    vector1.push_back(i);
    vector2.push_back(i);
  }

  // using built-in random generator:
  std::random_shuffle ( vector1.begin(), vector1.end() );
  std::random_shuffle ( vector2.begin(), vector2.end() );

  // print out content:
  std::cout << "vector1 contains:";
  for ( std::vector<int>::iterator it1 = vector1.begin(); it1 != vector1.end(); ++it1 )
    std::cout << ' ' << *it1;

  std::cout << '\n';
  std::cout << '\n';

  std::cout << "vector2 contains:";
  for ( std::vector<int>::iterator it2 = vector2.begin(); it2 != vector2.end(); ++it2 )
    std::cout << ' ' << *it2;

  std::cout << '\n';
  std::cout << '\n';

  return 0;
}

编辑这是我尝试实施的示例案例。在实践中,我有一个图像矢量和一个相应标签的矢量。我需要他们以同样的方式洗牌。请有人帮忙...... 非常感谢!!

8 个答案:

答案 0 :(得分:29)

不是将矢量自身混洗,而是将索引矢量混合到其他矢量中。由于您将为两者使用相同的索引,因此保证它们的顺序相同。

std::vector<int> indexes;
indexes.reserve(vector1.size());
for (int i = 0; i < vector1.size(); ++i)
    indexes.push_back(i);
std::random_shuffle(indexes.begin(), indexes.end());

std::cout << "vector1 contains:";
for ( std::vector<int>::iterator it1 = indexes.begin(); it1 != indexes.end(); ++it1 )
    std::cout << ' ' << vector1[*it1];

答案 1 :(得分:15)

确保对random_shuffle()的两次调用使用相同的种子:

auto seed = unsigned ( std::time(0) );

// ...

std::srand ( seed );
std::random_shuffle ( vector1.begin(), vector1.end() );

std::srand ( seed );
std::random_shuffle ( vector2.begin(), vector2.end() );

但请注意,标准未指定random_shuffle()应使用rand()函数生成随机排列 - 这是实现定义的。因此,srand()不会影响random_shuffle()关于不使用rand()的实施的结果。

random_shuffle()上C ++ 11标准的第25.3.12 / 4段规定:

  

备注:如果这些函数的实现使用随机数,那么   实施应使用以下随机来源:

     

函数的第一种形式的随机数的基础源是实现定义的。   实现可能使用标准C库中的rand函数。 [...]

因此,如果您想确保编写可移植代码,请使用接受随机数生成器作为第三个参数的random_shuffle()版本,以便您可以控制播种。

答案 2 :(得分:7)

正如其他人已经表明的那样,使用相同的种子重新播种应该允许您多次复制相同的shuffle。但是,如果您可以使用C ++ 11,我建议您不使用srand()random_shuffle()来实现此功能。相反,您应该将<random>库与std::shuffle一起使用。

首先,如果可能,应避免使用rand。除了它通常不是一个非常好的pRNG之外,由于共享状态,它还存在线程安全性问题。 <random>库通过让程序员明确控制pRNG状态并提供几个保证性能,大小和质量特性的选项来解决这两个问题。

其次,random_shuffle实际上并未指定使用rand,因此理论上使用srand重新播种不具备您想要的效果是合法的。要使用random_shuffle获得有保证的结果,您必须编写自己的生成器。转而shuffle修复了这一点,因为您可以直接使用标准引擎。

#include <algorithm> // shuffle, copy
#include <iostream>  // cout
#include <iterator>  // begin, end, ostream_iterator
#include <numeric>   // iota
#include <random>    // default_random_engine, random_device
#include <vector>    // vector

int main() {
  std::vector<int> v1(10);
  std::iota(begin(v1), end(v1), 1);
  auto v2 = v1;

  std::random_device r;
  std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};

  // create two random engines with the same state
  std::mt19937 eng1(seed);
  auto eng2 = eng1;

  std::shuffle(begin(v1), end(v1), eng1);
  std::shuffle(begin(v2), end(v2), eng2);

  std::copy(begin(v1), end(v1), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\n\n";
  std::copy(begin(v2), end(v2), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\n\n";
}

答案 3 :(得分:3)

你可以创建一个随机访问迭代器,如果它的dereferenced将std :: tuple返回给相应向量元素的引用。所以你可以把它们推到原地。或者你看看the boost version。 所以看起来应该是这样的:

std::random_shuffle(
  boost::make_zip_iterator(
    boost::make_tuple(vector1.begin(), vector2.begin())
  ),
  boost::make_zip_iterator(
    boost::make_tuple(vector1.end(), vector2.end()
  ),

);

这会将您的数据放在适当位置,使用两个以上的向量,如果您知道make_zip_iterator的作用,则自我记录。当然它应该比洗牌两次更快或使用第三个向量。

答案 4 :(得分:2)

在每次随机播放之前,使用可重现的值为伪随机数生成器播种。

std::srand ( 42 );
std::random_shuffle ( vector1.begin(), vector1.end() );
std::srand ( 42 );
std::random_shuffle ( vector2.begin(), vector2.end() );

答案 5 :(得分:2)

如果两者必须具有相同的顺序,为什么它们是分开的 引导?逻辑解决方案如下:

struct ImageData
{
    Image myImage;
    std::string myLabel;
    //  ...
};

然后你有一个ImageData的向量,你可以随机播放。

答案 6 :(得分:0)

不幸的是,如果我们使用srand,我们会更改内部种子值。我的意思是,下一个随机数将是预先确定的。并且,第一个决定:

std::srand ( 42 );
std::random_shuffle ( vector1.begin(), vector1.end() );
std::srand ( 42 );
std::random_shuffle ( vector2.begin(), vector2.end() );
std::srand ( unsigned ( std::time(0) ) );
// Post-code.

为后置代码保存rand。

第二个决定 - 它是Mark Ransom解决方案 - 它根本不会调用std :: srand(而且,我只是注意到它具有更高的性能)。

答案 7 :(得分:-1)

你为什么不写自己的shuffle:

for( size_t i = 0 ; i < numitems; ++i )
{
    size_t next = random() % numitems ;
    swap( v1[i], v1[next] );
    swap( v2[i], v2[next] );
}