У меня есть обучающий набор данных, из которого я хочу извлечь образцы случайным образом, чтобы все образцы использовались перед повторным случайным перемешиванием. По этой причине я реализовал простой генератор случайных индексов.
Для набора данных из 10 образцов результат выглядит примерно так:
0 1 2 3 4 5 6 7 8 9
4 3 7 8 0 5 2 1 6 9
0 5 7 8 4 3 9 2 1 6
5 7 6 3 8 4 2 0 1 9
4 6 0 2 8 1 3 9 5 7
4 0 5 1 7 9 6 2 8 3
3 8 5 6 1 7 2 4 0 9
0 4 6 2 9 5 8 3 1 7
1 3 6 8 2 7 5 9 0 4
5 1 7 9 8 0 6 4 2 3
Буду признателен за совет, особенно в следующих областях:
- Стиль кода (читабельность, соглашения об именах и т. Д.)
- Дизайн класса
- Эффективность (как избежать лишних сложностей)
- Изобретая колесо заново (предлагает ли STL функциональность, которую я должен использовать?)
- Возможно, есть ошибки, которых я сейчас не вижу?
Пожалуйста, будьте максимально усердны с этой реализацией и дайте мне конструктивный отзыв.
main.cpp
#include <iostream>
#include "random_index.hpp"
int main() {
unsigned int size = 10;
RandomIndex rand_idx(size);
unsigned int n = 0;
for (unsigned int i=0; i<100; ++i, ++n) {
std::cout << rand_idx.get_index() << ' ';
if ((n+1) % size == 0) {
std::cout << 'n';
}
}
std::cout << 'n';
}
random_index.cpp
#include "random_index.hpp"
RandomIndex::RandomIndex(unsigned int _size) {
size = _size;
index.resize(_size, 0);
std::iota(index.begin(), index.end(), 0);
}
unsigned int RandomIndex::get_index() {
if (counter < size) {
return index[counter++];
} else {
counter = 0;
std::random_shuffle(index.begin(), index.end());
return index[counter++];
}
}
random_index.hpp
#ifndef RANDOM_INDEX_H
#define RANDOM_INDEX_H
#include <vector>
#include <numeric>
#include <algorithm>
class RandomIndex {
public:
RandomIndex(unsigned int _size);
unsigned int get_index();
private:
unsigned int size;
unsigned int counter = 0;
std::vector<unsigned int> index;
};
#endif
Я скомпилировал код с помощью следующей команды:
g++ -O -Wall main.cpp random_index.cpp
1 ответ
Обзор
Ваш код создает один и тот же набор образцов каждый раз, когда вы random_shuffle
. Так что выход всегда
0 1 2 3 4 5 6 7 8 9
4 3 7 8 0 5 2 1 6 9
0 5 7 8 4 3 9 2 1 6
5 7 6 3 8 4 2 0 1 9
4 6 0 2 8 1 3 9 5 7
4 0 5 1 7 9 6 2 8 3
3 8 5 6 1 7 2 4 0 9
0 4 6 2 9 5 8 3 1 7
1 3 6 8 2 7 5 9 0 4
5 1 7 9 8 0 6 4 2 3
Вы должны инициализировать семя. Вы делаете это перед тем, как позвонить random_shuffle
. Если вы этого не сделаете, результат будет всегда один и тот же, поскольку он зависит от семени.
Вы можете исправить это, позвонив std::srand
в начале.
int main() {
std::srand(std::time(0));
}
Теперь вы можете увидеть разницу в выводе
0 1 2 3 4 5 6 7 8 9
9 2 8 3 6 4 0 7 1 5
8 9 4 6 7 2 1 5 0 3
4 6 0 2 3 1 5 8 9 7
2 3 0 1 6 8 9 5 7 4
8 9 6 5 2 1 3 7 4 0
9 3 2 8 6 7 5 0 4 1
8 1 2 5 9 6 4 3 0 7
7 1 4 6 8 9 3 0 5 2
3 1 9 4 2 7 6 0 5 8
Но верхний ряд всегда будет прежним. Проблема здесь
RandomIndex::RandomIndex(unsigned int _size) {
size = _size;
index.resize(_size, 0);
std::iota(index.begin(), index.end(), 0);
}
iota
всегда будет, начиная с 0
и увеличивая его, он достигнет index.end()
. get_index
перетасует контейнер только после первой строки, так как именно тогда counter < size
оценивается как ложь.
Чтобы исправить это, вы также можете перетасовать в начале, когда вы создаете вектор.
Использовать std::shuffle
random_shuffle
устарел. Использовать shuffle
с генератором случайных чисел, чтобы ваш код мог компилироваться в более поздних версиях C ++.
Избегать использования _
в качестве префикса для ваших переменных
Есть некоторые соглашения об именах вам нужно следовать, когда дело доходит до подчеркивания, чтобы избежать столкновений. Более того, это просто некрасиво выглядит. Для инициализации size
в конструкторе вашего класса вам лучше использовать списки инициализаторов членов.
RandomIndex::RandomIndex(unsigned int size)
: size(size) {
//...
}
использовать array
с фиксированным размером по вектору
В вашем классе size
это ценность, которая никогда не изменится. Если вы планируете сохранить его таким образом, используйте std::array
здесь с шаблоном, чтобы вы могли иметь фиксированный размер. Это позволит избежать изменения размера и возиться с кучей, что приведет к более быстрому выполнению. std::shuffle
будет работать так же, как и с vector
благодаря тому, как разработан STL.
template < size_t s >
class RandomIndex {
//...
private:
std::array < uint32_t, s > index;
};