Как обеспечить, чтобы два разных вектора перетасовывались в одном и том же порядке на С++?
У меня есть два вектора:
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;
}
EDIT Это пример, который я пытался реализовать. На практике у меня есть один вектор изображений и один вектор соответствующих меток. Мне нужно, чтобы их тасовали одинаково. Может кто-нибудь помочь...
Большое спасибо!
Ответы
Ответ 1
Вместо того, чтобы перетасовывать сами векторы, перетащите вектор индексов в другие векторы. Поскольку вы будете использовать те же индексы для обоих, они гарантированно будут в одном порядке.
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];
Ответ 2
Убедитесь, что вы используете одно и то же семя для обоих вызовов: 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()
.
Параграф 25.3.12/4 стандарта С++ 11 на random_shuffle()
указывает:
Примечания. В той мере, в которой реализация этих функций использует случайные числа, реализация должна использовать следующие источники случайности:
Исходный источник случайных чисел для первой формы функции определяется реализацией. Реализация может использовать функцию rand
из стандартной библиотеки C. [...]
Поэтому, если вы хотите убедиться, что вы пишете переносимый код, используйте версию random_shuffle()
, которая принимает генератор случайных чисел в качестве третьего аргумента, так что вы имеете контроль над посевом.
Ответ 3
Как показали другие, повторное посев с тем же семенем позволит вам повторить один и тот же случайный перехват несколько раз. Однако, если вы можете использовать С++ 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";
}
Ответ 4
Разделите генератор псевдослучайных чисел с воспроизводимым значением перед каждым перетасовкой.
std::srand ( 42 );
std::random_shuffle ( vector1.begin(), vector1.end() );
std::srand ( 42 );
std::random_shuffle ( vector2.begin(), vector2.end() );
Ответ 5
Вы можете создать итератор с произвольным доступом, который, если его разыменовывает, возвращает std:: tuple в ссылки элементов соответствующих векторов. Поэтому вы можете перетасовать их на место. Или вы смотрите ускоренную версию.
Поэтому он должен выглядеть примерно так:
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
. Конечно, он должен быть быстрее, чем перетасовать два раза или использовать третий вектор.
Ответ 6
Если оба должны иметь одинаковый порядок, почему они разделены
векторы? Логическим решением было бы что-то вроде:
struct ImageData
{
Image myImage;
std::string myLabel;
// ...
};
Затем у вас есть один вектор ImageData
, который вы перетасовываете.
Ответ 7
К сожалению, если мы используем 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 (и, как мне кажется, он имеет более высокую производительность).
Ответ 8
Почему бы вам не написать свой собственный тасовка:
for( size_t i = 0 ; i < numitems; ++i )
{
size_t next = random() % numitems ;
swap( v1[i], v1[next] );
swap( v2[i], v2[next] );
}