Генератор случайных индексов в C ++

У меня есть обучающий набор данных, из которого я хочу извлечь образцы случайным образом, чтобы все образцы использовались перед повторным случайным перемешиванием. По этой причине я реализовал простой генератор случайных индексов.

Для набора данных из 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 ответ
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;
   
};

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *