diff --git a/math.js b/math.js index 63f97f315..67ec7443d 100644 --- a/math.js +++ b/math.js @@ -7,7 +7,7 @@ * mathematical functions, and a flexible expression parser. * * @version 0.11.2-SNAPSHOT - * @date 2013-08-02 + * @date 2013-08-04 * * @license * Copyright (C) 2013 Jos de Jong @@ -439,28 +439,6 @@ var util = (function () { return res; }; - /** - * For each method for objects and arrays. - * In case of an object, the method loops over all properties of the object. - * In case of an array, the method loops over all indexes of the array. - * @param {Object | Array} object The object - * @param {function} callback Callback method, called for each item in - * the object or array with three parameters: - * callback(value, index, object) - */ - util.forEach = function forEach (object, callback) { - if (object instanceof Array) { - object.forEach(callback); - } - else { - for (var key in object) { - if (object.hasOwnProperty(key)) { - callback(object[key], key, object); - } - } - } - }; - /** * Creates a new object with the results of calling a provided function on * every prop in the object. @@ -7540,6 +7518,15 @@ var distributions = { } }; +/** + * Create a distribution object. + * @param {String} name Name of a distribution. + * Choose from 'uniform', 'normal'. + * @return {Object} distribution A distribution object containing functions: + * random([size, min, max]) + * randomInt([min, max]) + * pickRandom(array) + */ math.distribution = function(name) { if (!distributions.hasOwnProperty(name)) throw new Error('unknown distribution ' + name); @@ -7552,12 +7539,27 @@ math.distribution = function(name) { var randFunctions = { - random: function(min, max) { - if (arguments.length > 2) - newArgumentsError('random', arguments.length, 0, 2); - if (max === undefined) max = 1 - if (min === undefined) min = 0 - return min + distribution() * (max - min); + random: function(arg1, arg2, arg3) { + if (arguments.length > 3) + newArgumentsError('random', arguments.length, 0, 3); + + // Random matrix + else if (Object.prototype.toString.call(arg1) === '[object Array]') { + var min = arg2, max = arg3; + if (max === undefined) max = 1; + if (min === undefined) min = 0; + return new Matrix(_randomDataForMatrix(arg1, min, max)); + + // Random float + } else { + // TODO: more precise error message? + if (arguments.length > 2) + newArgumentsError('random', arguments.length, 0, 2); + var min = arg1, max = arg2; + if (max === undefined) max = 1; + if (min === undefined) min = 0; + return min + distribution() * (max - min); + } }, randomInt: function(min, max) { @@ -7570,13 +7572,6 @@ math.distribution = function(name) { if (arguments.length !== 1) newArgumentsError('pickRandom', arguments.length, 1); return possibles[Math.floor(Math.random() * possibles.length)]; - }, - - randomMatrix: function(size, min, max) { - if (arguments.length > 3 || arguments.length < 1) - newArgumentsError('pickRandom', arguments.length, 1, 3); - debugger - return new Matrix(_randomDataForMatrix(size, min, max)); } }; @@ -9463,7 +9458,7 @@ function isSupportedType(object) { name = token; fn = math.unary; getToken(); - params = [parse_pow(scope)]; + params = [parse_unary(scope)]; return new OperatorNode(name, fn, params); } diff --git a/src/function/probability/random.js b/src/function/probability/random.js index 1009041a6..aa618898d 100644 --- a/src/function/probability/random.js +++ b/src/function/probability/random.js @@ -39,7 +39,7 @@ var distributions = { * @param {String} name Name of a distribution. * Choose from 'uniform', 'normal'. * @return {Object} distribution A distribution object containing functions: - * random([min, max]) + * random([size, min, max]) * randomInt([min, max]) * pickRandom(array) */ @@ -55,12 +55,27 @@ math.distribution = function(name) { var randFunctions = { - random: function(min, max) { - if (arguments.length > 2) - newArgumentsError('random', arguments.length, 0, 2); - if (max === undefined) max = 1; - if (min === undefined) min = 0; - return min + distribution() * (max - min); + random: function(arg1, arg2, arg3) { + if (arguments.length > 3) + newArgumentsError('random', arguments.length, 0, 3); + + // Random matrix + else if (Object.prototype.toString.call(arg1) === '[object Array]') { + var min = arg2, max = arg3; + if (max === undefined) max = 1; + if (min === undefined) min = 0; + return new Matrix(_randomDataForMatrix(arg1, min, max)); + + // Random float + } else { + // TODO: more precise error message? + if (arguments.length > 2) + newArgumentsError('random', arguments.length, 0, 2); + var min = arg1, max = arg2; + if (max === undefined) max = 1; + if (min === undefined) min = 0; + return min + distribution() * (max - min); + } }, randomInt: function(min, max) { @@ -73,12 +88,6 @@ math.distribution = function(name) { if (arguments.length !== 1) newArgumentsError('pickRandom', arguments.length, 1); return possibles[Math.floor(Math.random() * possibles.length)]; - }, - - randomMatrix: function(size, min, max) { - if (arguments.length > 3 || arguments.length < 1) - newArgumentsError('pickRandom', arguments.length, 1, 3); - return new Matrix(_randomDataForMatrix(size, min, max)); } }; diff --git a/test/function/probability/random.js b/test/function/probability/random.js index 44a98e67a..b368d1c52 100644 --- a/test/function/probability/random.js +++ b/test/function/probability/random.js @@ -8,38 +8,96 @@ var assertApproxEqual = function(testVal, val, tolerance) { else assert.ok(diff <= tolerance) } -var testRandom = function() { +var assertUniformDistribution = function(values, min, max) { + var interval = (max - min) / 10 + count = _.filter(values, function(val) { return val < min }).length + assert.equal(count, 0) + count = _.filter(values, function(val) { return val > max }).length + assert.equal(count, 0) + + count = _.filter(values, function(val) { return val < (min + interval) }).length + assert.equal(math.round(count/values.length, 1), 0.1) + count = _.filter(values, function(val) { return val >= (min + interval) && val < (min + 2 * interval) }).length + assert.equal(math.round(count/values.length, 1), 0.1) + count = _.filter(values, function(val) { return val >= (min + 2 * interval) && val < (min + 3 * interval) }).length + assert.equal(math.round(count/values.length, 1), 0.1) + count = _.filter(values, function(val) { return val >= (min + 3 * interval) && val < (min + 4 * interval) }).length + assert.equal(math.round(count/values.length, 1), 0.1) + count = _.filter(values, function(val) { return val >= (min + 4 * interval) && val < (min + 5 * interval) }).length + assert.equal(math.round(count/values.length, 1), 0.1) + count = _.filter(values, function(val) { return val >= (min + 5 * interval) && val < (min + 6 * interval) }).length + assert.equal(math.round(count/values.length, 1), 0.1) + count = _.filter(values, function(val) { return val >= (min + 6 * interval) && val < (min + 7 * interval) }).length + assert.equal(math.round(count/values.length, 1), 0.1) + count = _.filter(values, function(val) { return val >= (min + 7 * interval) && val < (min + 8 * interval) }).length + assert.equal(math.round(count/values.length, 1), 0.1) + count = _.filter(values, function(val) { return val >= (min + 8 * interval) && val < (min + 9 * interval) }).length + assert.equal(math.round(count/values.length, 1), 0.1) + count = _.filter(values, function(val) { return val >= (min + 9 * interval) }).length + assert.equal(math.round(count/values.length, 1), 0.1) + +} + +var testRandomFloat = function() { + var picked = [], count + + _.times(1000, function() { + picked.push(math.random()) + }) + assertUniformDistribution(picked, 0, 1) +} + +var testRandomFloatMinMax = function() { var picked = [], count _.times(1000, function() { picked.push(math.random(-10, 10)) }) + assertUniformDistribution(picked, -10, 10) +} - count = _.filter(picked, function(val) { return val < -10 }).length - assert.equal(count, 0) - count = _.filter(picked, function(val) { return val > 10 }).length - assert.equal(count, 0) +var testRandomMatrix = function() { + var picked = [], + matrices = [], + size = [2, 3, 4], + count, matrix - count = _.filter(picked, function(val) { return val < -8 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= -8 && val < -6 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= -6 && val < -4 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= -4 && val < -2 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= -2 && val < 0 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= 0 && val < 2 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= 2 && val < 4 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= 4 && val < 6 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= 6 && val < 8 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= 8 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) + _.times(100, function() { + matrices.push(math.random(size)) + }) + + // Collect all values in one array + matrices.forEach(function(matrix) { + assert.deepEqual(matrix.size(), size) + matrix.forEach(function(val) { + picked.push(val) + }) + }) + assert.equal(picked.length, 2 * 3 * 4 * 100) + + assertUniformDistribution(picked, 0, 1) +} + +var testRandomMatrixMinMax = function() { + var picked = [], + matrices = [], + size = [2, 3, 4], + count, matrix + + _.times(100, function() { + matrices.push(math.random(size, -103, 8)) + }) + + // Collect all values in one array + matrices.forEach(function(matrix) { + assert.deepEqual(matrix.size(), size) + matrix.forEach(function(val) { + picked.push(val) + }) + }) + assert.equal(picked.length, 2 * 3 * 4 * 100) + + assertUniformDistribution(picked, -103, 8) } var testRandomInt = function() { @@ -102,52 +160,6 @@ var testPickRandom = function() { assert.equal(math.round(count/picked.length, 1), 0.2) } -var testRandomMatrix = function(size, min, max) { - var picked = [], - matrices = [], - size = [2, 3, 4], - count, matrix - - _.times(100, function() { - matrices.push(math.randomMatrix(size, -10, 10)) - }) - - // Collect all values in one array - matrices.forEach(function(matrix) { - assert.deepEqual(matrix.size(), size) - matrix.forEach(function(val) { - picked.push(val) - }) - }) - assert.equal(picked.length, 2 * 3 * 4 * 100) - - count = _.filter(picked, function(val) { return val < -10 }).length - assert.equal(count, 0) - count = _.filter(picked, function(val) { return val > 10 }).length - assert.equal(count, 0) - - count = _.filter(picked, function(val) { return val < -8 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= -8 && val < -6 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= -6 && val < -4 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= -4 && val < -2 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= -2 && val < 0 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= 0 && val < 2 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= 2 && val < 4 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= 4 && val < 6 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= 6 && val < 8 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) - count = _.filter(picked, function(val) { return val >= 8 }).length - assert.equal(math.round(count/picked.length, 1), 0.1) -} - var testRandomNormal = function() { var picked = [], count, distribution = math.distribution('normal') @@ -171,9 +183,13 @@ var testRandomNormal = function() { assertApproxEqual(count/picked.length, 0.93, 0.01) } -testRandom() +testRandomFloat() +testRandomFloatMinMax() +testRandomMatrix() +testRandomMatrixMinMax() + + testRandomInt() testPickRandom() -testRandomMatrix() testRandomNormal() \ No newline at end of file