Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a random number generating member function doesn't produce entirely random numbers

I'm creating a wxWidget application with C++ where at the start of the program I want the application window to contain pixels with random colors like this:

Random coloured pixels

In the above application there are 3600 pixels (60 x 60) and I have given each pixel a random RGB color by using uniform_int_distribution

The colours for the pixels in the image above are generated at the moment using the following function in my code:

void random_colors(int ctable[][3], int n)
{
  // construct a trivial random generator engine from a time-based seed:
  unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
  std::default_random_engine generator (seed);
  std::uniform_int_distribution<int> distribution(0,255);
  for(int i=0; i<n; i++)
  {
      for(int j=0; j<3; j++)
      {
        ctable[i][j] = distribution(generator);
      }
  }
}

I do this by giving this function a table with dimensions 3600 x 3 and this function will fill in the values for the colours.

This way however is not what I want. What I want is to create a class called somNode where each somNode-object represents a pixel in the picture (with RGB values as a member array attribute). In this somNode-class I have a member function using uniform_int_distribution to give when constructed each somNode its own random RGB colour. This is the function which creates the random colour for each somNode:

void rand_node_colour(int nodeWeights[])
{
  // construct a trivial random generator engine from a time-based seed:
  unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
  std::default_random_engine generator (seed);
  std::uniform_int_distribution<int> distribution(0,255);
  for(int i=0; i<3; i++)
  {
    nodeWeights[i] = distribution(generator);
  }
}

the nodeWeights member array represents the RGB-value of the somNode. Now when I create this "som-grid" what I have in the image above (the 3600 pixels corresponding to 3600 somNodes) I use the following code (take a look at the som-constructor):

#include "somNode.h"
#include <vector>

class som
{
    public:
        double learning_rate;
        std::vector<somNode> somGrid;
    public:
        som(double lrate);
        void epoch();
        void trainOnce();

};

/*
* Initialize the som grid
*/
som::som(double lrate)
{
    learning_rate = lrate;
    // Create the som grid
    for(int i=0; i<60; i++)
    {
        for(int j=0; j<60; j++)
        {
            int xL = j*10;
            int xR = (j+1)*10;
            int yT = i*10;
            int yB = (i+1)*10;
            somGrid.push_back(somNode(xL, xR, yB, yT));
        }
    }
}

// Train som by one epoch
void som::epoch()
{

}

// Train som by one color
void som::trainOnce()
{

}

So I have a vector<somNode> somGrid where I push all these 3600 somNodes when I construct them. When each node is constructed the somNode member function rand_node_colour is called which creates the RGB-value.

When however I implement this code instead of the one I used at first I get this result:

enter image description here

You can see that there is a clear pattern so something is going wrong here. My question is: What is happening in the random number generation when somNodes are created? Why it doesn't produce the same result as the code I used above?

P.S. here is the somNode.cpp:

#include <random>
#include <iostream>
#include <chrono>
#include<cmath>

void rand_node_colour(int nodeWeights[]);

/*
* This class represent a node in the som-grid
*/
class somNode
{
    public:
        // Weight of the node representing the color
        int nodeWeights[3];
        // Position in the grid
        double X, Y;
        // corner coorinates for drawing the node on the grid
        int x_Left, x_Right, y_Bottom, y_Top;

    public:
        // Constructor
        somNode(int xL, int xR, int yB, int yT);
        void editWeights(int r, int g, int b);
        double getDistance(int r, int g, int b);
};


somNode::somNode(int xL, int xR, int yB, int yT)
{
    // Set the corner points
    x_Left = xL;
    x_Right = xR;
    y_Bottom = yB;
    y_Top = yT;
    // Initialize random weights for node
    rand_node_colour(nodeWeights);
    // Calculate the node's position (center coordinate)
    X = x_Left + (double)((x_Right - x_Left)/double(2));
    Y = y_Bottom + (double)((y_Top - y_Bottom)/double(2));
}

void somNode::editWeights(int r, int g, int b)
{
    nodeWeights[0] = r;
    nodeWeights[1] = g;
    nodeWeights[2] = b;
}

double somNode::getDistance(int r, int g, int b)
{
    return sqrt(pow(nodeWeights[0]-r, 2) + pow(nodeWeights[1]-g, 2) + pow(nodeWeights[2]-b, 2));
}


void rand_node_colour(int nodeWeights[])
{
  // construct a trivial random generator engine from a time-based seed:
  unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
  std::default_random_engine generator (seed);
  std::uniform_int_distribution<int> distribution(0,255);
  for(int i=0; i<3; i++)
  {
    nodeWeights[i] = distribution(generator);
  }
}
like image 699
jjepsuomi Avatar asked Jun 08 '16 11:06

jjepsuomi


1 Answers

The issue here is that you constantly recreate and seed the random number generator in rand_node_colour. You call it in a tight loop so you can get the same time which means the seed will be the same and that means the random numbers generated will be the same.

What you need to do is seed the generator once and then keep using its random output. An easy way to fix you code would be to make it static in the function sos it is only initialized once and each subsequent call to the function will continue on instead of start the generator all over. If we do that the code becomes

void rand_node_colour(int nodeWeights[])
{
  // construct a trivial random generator engine from a time-based seed:
  static std::default_random_engine generator (std::chrono::system_clock::now().time_since_epoch().count());
  std::uniform_int_distribution<int> distribution(0,255);
  for(int i=0; i<3; i++)
  {
    nodeWeights[i] = distribution(generator);
  }
}
like image 76
NathanOliver Avatar answered Oct 23 '22 03:10

NathanOliver