Search code examples
c++random-seedperceptronuniform-distribution

Why does std::rand always generate 0?


I'm implementing a single-layer perceptron and there is a problem with my weight_coef function such as it is generating 0 every time the function is called. Thus I get inaccurate results when calculating activation. Here is a snippet of my weight_coef function, which is supposed to generate a number between 0 and 1:

#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

using namespace std;

//Helper function to generate a random number between 0 and 1
double weight_coef() {
    //((double)rand() / RAND_MAX);
    srand(time(NULL));
    double random = static_cast<double>(rand() / RAND_MAX);
    random = round(random * 100) / 100.0; //to 2 dec points
    return random;
}

int main() {
    // Set up the variables
    vector<double> w = { weight_coef(), weight_coef(), weight_coef()};
}

Would be grateful if you could suggest what's wrong.


Solution

  • The problem is that std::rand returns an integer that is guaranteed to be between 0 and RAND_MAX. Thus the weight coefficient code

    double weight_coef() {
        srand(time(NULL));
        double random = static_cast<double>(rand() / RAND_MAX); // <-- LOOK
        /* The above line will always set random to 0 */
        random = round(random * 100) / 100.0;
        return random;
    }
    

    will always return 0 (actually it will almost always be 0, and will be be 1 with probability 1/RAND_MAX). One solution would be to take the random integer, mod it by 100, and then interpret that as your two decimal digit probability. But note that this will be slightly nonuniform, as RAND_MAX is probably not divisible by 100 (and is likely 2^31 - 1 = 2147483647).

    If you want to make a uniform random variable, you can do something like

    #include <iostream>
    #include <random>
    
    const int LOWER_BOUND = 0;
    const int UPPER_BOUND = 100;
    
    std::random_device rand_dev;
    std::mt19937 gen;
    std::uniform_int_distribution<int> dist;
    
    int random_int() {
      return dist(gen);
    }
    
    int main()
    {
      gen = std::mt19937(rand_dev());
      dist = std::uniform_int_distribution<int>(LOWER_BOUND, UPPER_BOUND);
    
      for (int i = 0; i < 10; i++) {
          std::cout << random_int() << "\n";   // generate 10 random integers
      }
    }
    

    I note it is also not a good idea to repeatedly reinitialize the seed on every function call. Instead, set the seed once - possibly in main (as done implicitly in the code snippet I posted).