Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > default_random_engine и классы


Автор: Luyan 15.12.2013, 13:48
Доброго времени суток.
Использовал в С++ прошлого стандарта rand(), а srand() делал глобально.
srand() менял в цикле и получал различные результаты (для усреднения и тд).
Есть проект, несколько классов и все вызывают rand() в различных местах.
Но теперь появилась потребность в некоторых классах кое-где использовать нормальное распеределение, вместо равномерного. Поэтому начал переводить код на C++11 с их рандомом и использовать uniform_real_distribution и normal_distribution.
Вот как было раньше, упрощенный пример:
Код

#include <iostream>
#include <cstdlib>

class B
{
public:
  B(){}
  int random()
  {
    return rand()%100;
  }
  // ....
};

class A
{
public:
  A(){}
  void load()
  {
    B obj2;
    std::cout << obj2.random() << std::endl;
  }
  // ....
};

int main()
{
  int n = 10; 
  A obj;
  
  for(int i = 0; i < n; i++)
  {
    // ....
    srand(1+i);
    obj.load();
    // ....
  }
}


А вот как я пытаюсь делать сейчас:
Код

#include <iostream>
#include <random>

class B
{
  std::default_random_engine generator_b;
public:
  B(std::default_random_engine& t)
  {
    generator_b = t;
  }
  int random()
  {
    std::uniform_int_distribution<int> dist(0,9);
    return dist(generator_b);
  }
};

class A
{
protected:
  std::default_random_engine generator_a;
public:
  A():generator_a(std::default_random_engine()){}
  void set_seed(int t)
  {
    generator_a.seed(t);
  }
  void load()
  {
    B obj2(generator_a);
    std::cout << obj2.random() << std::endl;
  }
};

int main()
{
  int n = 10;
  
  A obj;
  for(int i = 0; i < n; i++)
  {
    // ....
    obj.set_seed(1+i);
    obj.load();
    // ....
  }
}


Но это почему-то не работает - выдает нули. В чем ошибка?
Как я понимаю, в C++11 надо везде таскать за собой generator?
Или есть другие способы, кроме глобального объявления? 

Автор: akizelokro 15.12.2013, 17:26
Код

  int random()
  {
    std::uniform_int_distribution<int> dist(0,9);
    return dist(generator_b);
  }


Каждый раз создаётся std::uniform_int_distribution<int>, попробуй его сделать членом класса B, а создание перенести в конструктор.

Автор: Luyan 15.12.2013, 19:24
Цитата(akizelokro @  15.12.2013,  17:26 Найти цитируемый пост)
создание перенести в конструктор

Создание в смысле установки параметров? или сделать поле dist указателем и через new в конструкторе выделять?
сделал так:
Код

class B
{
  std::default_random_engine generator_b;
  std::uniform_int_distribution<int> dist;
public:
  B(std::default_random_engine& t):generator_b(t),dist(0,9)
  {
    std:: cout << "a: " << dist.a() << ", b: " << dist.b() << std::endl;   
  }
  int random()
  {
    return dist(generator_b);
  }
};

Код все равно выдает 0:
Код

a: 0, b: 9
0
a: 0, b: 9
0
a: 0, b: 9
0
a: 0, b: 9
0
a: 0, b: 9
0
a: 0, b: 9
0
a: 0, b: 9
0
a: 0, b: 9
0
a: 0, b: 9
0
a: 0, b: 9
0

А generator типа default_random_engine можно просто делать членом класса? или есть какие-нибудь ограничения? похоже в этом проблема. В сети ничего стоящего по этой теме не могу найти, все останавливаются на примерчиках в main.

Автор: akizelokro 15.12.2013, 21:30
Да просто и genertor'ы и все виды distribution'ов в новом стандарте уже сами классы, в стандарте уже сделана работа, которую ты пытаешься повторить, по идее просто делай, как указано в примерах. Потому что по моему имхо ты делаешь не совсем нужное усложнение.
Потом же смотри, судя по твоему листингу у тебя постоянно идёт сообщение из конструктора
Код

a: 0, b: 9

то есть, ты ещё где-то код изменил. Смысл моего предложения был в том, чтобы uniform_int_distribution не пересоздавался каждый раз, потому что с этой мутью я уже сталкивался в том плане, что эти генераторы и распределения псевдослучайны и тупо повторяютя на первом шагу. А если бы ты брал второй шаг, было бы уже другое 
Понял, это у тебя в
Код

  void load()
  {
    B obj2;
    std::cout << obj2.random() << std::endl;
  }


где ты при каждом вызове создаёшь класс B по новой. Сделай экземпляр класса B членом класса А и посмотри.
Код

class A {
...
B obj2; // здесь добавил
...
  void load()
  {
//    B obj2(generator_a); - здесь убрал
    std::cout << obj2.random() << std::endl;
  }

Автор: Luyan 15.12.2013, 22:33
Цитата(akizelokro @  15.12.2013,  21:30 Найти цитируемый пост)
эти генераторы и распределения псевдослучайны и тупо повторяютя на первом шагу

Вот за эту фразу большое спасибо. Проверил, действительно:
Код

int main()
{
  int n = 10;
  std::default_random_engine generator;
  std::uniform_int_distribution<int> dist(0,9);
  //std::uniform_real_distribution<double> dist(0.0,1.0);
  
  for(int i = 0; i < n; i++)
  {
    generator.seed(1+i);
    for(int j = 0; j < 5; j++)
      std::cout << dist(generator) << ' ';
    std::cout << std::endl;
  }
}

Выдает:
Код

0 1 7 4 5 
0 2 5 9 0 
0 3 2 3 5 
0 5 0 8 1 
0 6 7 2 6 
0 7 5 7 1 
0 9 2 2 7 
0 0 0 6 2 
0 1 8 1 7 
0 3 5 5 3

Хорошо, что это не справедливо для вещественных типов:
Код

0.131538 0.45865 0.218959 0.678865 0.934693 
0.263076 0.9173 0.437918 0.357729 0.869386 
0.394613 0.37595 0.656878 0.0365942 0.804079 
0.526151 0.834601 0.875837 0.715459 0.738772 
0.657689 0.293251 0.0947959 0.394324 0.673464 
0.789227 0.751901 0.313755 0.0731883 0.608157 
0.920765 0.210551 0.532714 0.752053 0.54285 
0.0523023 0.669201 0.751673 0.430918 0.477543 
0.18384 0.127851 0.970633 0.109782 0.412236 
0.315378 0.586501 0.189592 0.788647 0.346929


Короче, с классами тоже все работает. Спасибо.

Автор: akizelokro 16.12.2013, 12:19
Да я столкнулся сам как-то раз с таким поведением для Visual Studio, правда, как я от этого ушёл для int'ов, надо смотреть. А тут у тебя наслоилось всё это, что ты "дёргал" только первую цифру. Ну, я и подумал, что это из той темы, а даже не из ошибок, но до конца не был уверен, потому что дело было давно. Скажем так, что-то меня подталкивало подозревать..

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)