Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
'use strict';

// NOTE: distribution is NOT added to math.distribution but returned by the factory function
// TODO: rethink math.distribution

module.exports = function (math) {
  var Matrix = math.type.Matrix;
  var array = require('../../util/array');
  var collection = math.collection;
  var isCollection = collection.isCollection;

  /**
   * Create a distribution object with a set of random functions for given
   * random distribution.
   *
   * Syntax:
   *
   *     math.distribution(name)
   *
   * Examples:
   *
   *     var normalDist = math.distribution('normal'); // create a normal distribution
   *     normalDist.random(0, 10);                      // get a random value between 0 and 10
   *
   * See also:
   *
   *     random, randomInt, pickRandom
   *
   * @param {String} name   Name of a distribution. Choose from 'uniform', 'normal'.
   * @return {Object}       Returns a distribution object containing functions:
   *                        `random([size] [, min] [, max])`,
   *                        `randomInt([min] [, max])`,
   *                        `pickRandom(array)`
   */
  function distribution(name) {
    if (!distributions.hasOwnProperty(name))
      throw new Error('Unknown distribution ' + name);

    var args = Array.prototype.slice.call(arguments, 1),
        distribution = distributions[name].apply(this, args);

    return (function(distribution) {

      // This is the public API for all distributions
      var randFunctions = {

        random: function(arg1, arg2, arg3) {
          var size, min, max;
          if (arguments.length > 3) {
            throw new math.error.ArgumentsError('random', arguments.length, 0, 3);

          // `random(max)` or `random(size)`
          } else if (arguments.length === 1) {
            if (isCollection(arg1)) {
              size = arg1;
            }
            else {
              max = arg1;
            }
          // `random(min, max)` or `random(size, max)`
          } else if (arguments.length === 2) {
            if (isCollection(arg1)) {
              size = arg1;
              max = arg2;
            }
            else {
              min = arg1;
              max = arg2;
            }
          // `random(size, min, max)`
          } else {
            size = arg1;
            min = arg2;
            max = arg3;
          }

          // TODO: validate type of min, max, and size

          if (max === undefined) max = 1;
          if (min === undefined) min = 0;
          if (size !== undefined) {
            var res = _randomDataForMatrix(size.valueOf(), min, max, _random);
            return (size instanceof Matrix) ? math.matrix(res) : res;
          }
          else return _random(min, max);
        },

        randomInt: function(arg1, arg2, arg3) {
          var size, min, max;
          if (arguments.length > 3 || arguments.length < 1)
            throw new math.error.ArgumentsError('randomInt', arguments.length, 1, 3);

          // `random(max)` or `random(size)`
          else if (arguments.length === 1)
            if (isCollection(arg1)) {
              size = arg1;
            }
            else {
              max = arg1;
            }
          // `randomInt(min, max)` or `randomInt(size, max)`
          else if (arguments.length === 2) {
            if (isCollection(arg1)) {
              size = arg1;
              max = arg2;
            }
            else {
              min = arg1;
              max = arg2;
            }
          // `randomInt(size, min, max)`
          } else {
            size = arg1;
            min = arg2;
            max = arg3;
          }

          // TODO: validate type of min, max, and size

          if (min === undefined) min = 0;
          if (size !== undefined) {
            var res = _randomDataForMatrix(size.valueOf(), min, max, _randomInt);
            return (size instanceof Matrix) ? math.matrix(res) : res;
          }
          else return _randomInt(min, max);
        },

        pickRandom: function(possibles) {
          if (arguments.length !== 1) {
            throw new math.error.ArgumentsError('pickRandom', arguments.length, 1);
          }
          if (possibles instanceof Matrix) {
            possibles = possibles.valueOf(); // get Array
          }
          else if (!Array.isArray(possibles)) {
            throw new math.error.UnsupportedTypeError('pickRandom', math['typeof'](possibles));
          }

          if (array.size(possibles).length > 1) {
            throw new Error('Only one dimensional vectors supported');
          }

          // TODO: add support for multi dimensional matrices
          return possibles[Math.floor(Math.random() * possibles.length)];
        }

      };

      var _random = function(min, max) {
        return min + distribution() * (max - min);
      };

      var _randomInt = function(min, max) {
        return Math.floor(min + distribution() * (max - min));
      };

      // This is a function for generating a random matrix recursively.
      var _randomDataForMatrix = function(size, min, max, randFunc) {
        var data = [], length, i;
        size = size.slice(0);

        if (size.length > 1) {
          for (i = 0, length = size.shift(); i < length; i++)
            data.push(_randomDataForMatrix(size, min, max, randFunc));
        } else {
          for (i = 0, length = size.shift(); i < length; i++)
            data.push(randFunc(min, max));
        }

        return data;
      };

      return randFunctions;

    })(distribution);
  };

  // Each distribution is a function that takes no argument and when called returns
  // a number between 0 and 1.
  var distributions = {

    uniform: function() {
      return Math.random;
    },

    // Implementation of normal distribution using Box-Muller transform
    // ref : http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
    // We take : mean = 0.5, standard deviation = 1/6
    // so that 99.7% values are in [0, 1].
    normal: function() {
      return function() {
        var u1, u2,
            picked = -1;
        // We reject values outside of the interval [0, 1]
        // TODO: check if it is ok to do that?
        while (picked < 0 || picked > 1) {
          u1 = Math.random();
          u2 = Math.random();
          picked = 1/6 * Math.pow(-2 * Math.log(u1), 0.5) * Math.cos(2 * Math.PI * u2) + 0.5;
        }
        return picked;
      }
    }
  };

  return distribution;
};
New to GrepCode? Check out our FAQ X