//** G/G/s/c/p JavaScript Code (commented version): Random distributions ** // (c) Jaroslav Sklenar //====================== Theoretical Distributions ======================= function uniform(min,max) { var r = Math.random(); return min + r * (max - min) }; //......................................................................... function exponential(mean) { var r = Math.random(); return - mean * Math.log(r); }; //......................................................................... function normal(mean, std) { var x = 0; for (var i = 1; i <= 12; i++) { x += Math.random(); }; return (x - 6)*std + mean; }; //====================== User Defined Distribution ======================= function distribution(name) { // Constructor this.dname = name; // Properties this.xv = new Array(); // Values this.px = new Array(); // Probabilities this.fx = new Array(); // Cumulative Distribution this.size = -1; this.status = "Table Empty"; this.confirmed = false; this.disttype = "User"; // Types: User,Exp,Uni,Norm this.enterMode = "Prob"; // Default modes this.generMode = "Cont" // Others: Cumul, Discr this.prompt1 = "Not used"; // Others: Mean,Min this.par1 = 0; this.prompt2 = "Not used"; // Others: Std,Max this.par2 = 0; }; //............................... methods ................................. function update(index) { with (this) { switch (index) { case 0 : disttype = "User"; prompt1 = "Not used"; par1 = 0; prompt2 = "Not used"; par2 = 0; break; case 1 : disttype = "Exp"; prompt1 = "Mean"; par1 = 1; prompt2 = "Not used"; par2 = 0; break; case 2 : disttype = "Uni"; prompt1 = "Min"; par1 = 0; prompt2 = "Max"; par2 = 1; break; case 3 : disttype = "Norm"; prompt1 = "Mean"; par1 = 3; prompt2 = "Std Dev"; par2 = 1; break; default : alert("Index out of range - update."); } return true; }}; distribution.prototype.update = update; //......................................................................... function testpar1(tx) { with (this) { var x = parseFloat(tx); if (! syntaxOK(x)) { return false }; switch (disttype) { case "User" : alert("Parameter not used."); return false; case "Exp" : if (x <= 0) { alert("The mean value must be positive."); return false; }; break; case "Uni" : if (x < 0 || x >= par2) { alert("The minimum value must be non-negative and smaller than the maximum."); return false; }; break; case "Norm" : if (x <= 0) { alert("The mean value must be positive."); return false; }; break; default : alert("Index out of range - update."); }; par1 = x; return true; }}; distribution.prototype.testpar1 = testpar1; //......................................................................... function testpar2(tx) { with (this) { var x = parseFloat(tx); if (! syntaxOK(x)) { return false }; switch (disttype) { case "User" : alert("Parameter not used."); return false; case "Exp" : alert("Parameter not used."); return false; case "Uni" : if (x < 0 || x <= par1) { alert("The maximum value must be greater than the minimum."); return false; }; break; case "Norm" : if (x <= 0) { alert("The Standard Deviation must be positive."); return false; }; break; default : alert("Index out of range - update."); }; par2 = x; return true; }}; distribution.prototype.testpar2 = testpar2; //......................................................................... function fixtable() { with (this) { if (enterMode=="Prob") { // Entering probabilities fx[0] = px[0]; for (var i = 1; i <= size; i++) { fx[i] = fx[i-1] + px[i] } } else { // Entering distr. function px[0] = fx[0]; for (var i = 1; i <= size; i++) { px[i] = fx[i] - fx[i-1] } }; return true; }}; distribution.prototype.fixtable = fixtable; //......................................................................... function last() { with (this) { if (size < 0) { return 0 } else { return xv[size]; } }}; distribution.prototype.last = last; //......................................................................... function compareWithLastValue(textElement) { with (this) { var x = parseFloat(textElement); if (! syntaxOK(x)) { return false }; if (size >= 0) { if (x < xv[size]) { alert("The value ( " + x + " ) must not be smaller than the previous one ( " + xv[size] + " )."); return false; } }; return true; }}; distribution.prototype.compareWithLastValue = compareWithLastValue; //......................................................................... function showTable() { with (this) { var y = " # x p(x) F(x)" + "\n" + " ===================================" + "\n"; var s = ""; for (var i = 0; i <= size; i++) { var l = " " + i; l += spaces(4-l.length) + xv[i]; s = "" + px[i]; l += spaces(14-l.length) + s.slice(0,12); s = "" + fx[i]; l += spaces(28-l.length) + s.slice(0,12) + "\n"; y += l; }; if (size==-1) { status = "Table Empty"; } else { if (confirmed) { status = "Table OK"; } else { status = "Editing Table"; } }; return y; }}; distribution.prototype.showTable = showTable; //......................................................................... function accept(tx,tp) { with (this) { if (enterMode == "Prob") { // Entering probabilities confirmed = false; size++; xv[size] = parseFloat(tx); px[size] = parseFloat(tp); if (size==0) { fx[size] = px[size]; } else { fx[size] = fx[size-1] + px[size]; } } else { // Entering distr. function var fxx = parseFloat(tp); if (fxx < fx[size]) { alert("The F(x) value ( " + fxx + " ) must not be smaller than the previous one ( " + fx[size] + " )."); return false; } else { confirmed = false; size++; xv[size] = parseFloat(tx); fx[size] = fxx; if (size==0) { px[size] = fx[size]; } else { px[size] = fx[size] - fx[size-1]; } } }; return true; }}; distribution.prototype.accept = accept; //......................................................................... function edit() { with (this) { var n = parseInt(prompt("Enter the number of the entry to be edited:")); if ((isNaN(n))||(n<0)||(n>size)) { alert("Not a correct entry number or empty table."); return false; }; var x = parseFloat(prompt("Enter the new value (x) of the entry " + n + " :",xv[n])); if (! syntaxOK(x)) { return false }; if (enterMode=="Prob") { // Entering probabilities var p = parseFloat(prompt("Enter the new probability (p) of the entry " + n + " :",px[n])); } else { // Entering distr. function var p = parseFloat(prompt("Enter the new cumulative probability (F) of the entry " + n + " :",fx[n])) }; if (isNaN(p)) { alert("Syntax error - the probability is not a number."); return false; }; if ((p < 0) || (p > 1)) { alert("The probability must be a non-negative number not greater than 1."); return false }; // All seems OK confirmed = false; xv[n] = x; if (enterMode=="Prob") { // Entering probabilities px[n] = p; } else { fx[n] = p; }; fixtable(); return true; }}; distribution.prototype.edit = edit; //......................................................................... function insert() { with (this) { var n = parseInt(prompt("Enter the number of the entry to be inserted (the current entry with this number and all succeeding will be moved):")); if ((isNaN(n))||(n<0)||(n>size)) { alert("Not a correct entry number or empty table."); return false; }; var x = parseFloat(prompt("Enter the value (x) of the entry " + n + " :",xv[n])); if (! syntaxOK(x)) { return false }; if (enterMode=="Prob") { // Entering probabilities var p = parseFloat(prompt("Enter the probability (p) of the entry " + n + " :",px[n])); } else { // Entering distr. function var p = parseFloat(prompt("Enter the cumulative probability (F) of the entry " + n + " :",fx[n])) }; if (isNaN(p)) { alert("Syntax error - the probability is not a number."); return false; }; if ((p < 0) || (p > 1)) { alert("The probability must be a non-negative number not greater than 1."); return false; }; // All seems OK confirmed = false; size++; for (var i = size; i > n; i--) { // Shift all up xv[i] = xv[i-1]; px[i] = px[i-1]; fx[i] = fx[i-1]; }; xv[n] = x; if (enterMode=="Prob") { // Entering probabilities px[n] = p; } else { fx[n] = p; }; fixtable(); return true; }}; distribution.prototype.insert = insert; //......................................................................... function deleteit() { with (this) { var n = parseInt(prompt("Enter the number of the entry to be deleted:")); if ((isNaN(n))||(n<0)||(n>size)) { alert("Not a correct entry number or empty table."); return false }; confirmed = false; if (size==0) { // one item only size = -1; xv.length = 0; // truncating the arrays px.length = 0; fx.length = 0; } else { for (var i = n; i <= size-1; i++) { // Move the rest down xv[i] = xv[i+1]; px[i] = px[i+1]; fx[i] = fx[i+1]; }; size--; fixtable() }; return true; }}; distribution.prototype.deleteit = deleteit; //......................................................................... function cleartable() { with (this) { size = -1; xv.length = 0; px.length = 0; fx.length = 0; confirmed = false; return true; }}; distribution.prototype.cleartable = cleartable; //......................................................................... function confirmtable() { with (this) { confirmed = false; if (size == -1) { alert("The table is empty !"); return false; }; for (var i = 1; i <= size; i++) { if (xv[i] < xv[i-1]) { alert("The values (x) in the table are not in non-decreasing order. This has to be corrected."); return false; } }; if (enterMode=="Prob") { // Entering probabilities fx[0] = px[0]; var sofarOK = true; for (var i = 1; i <= size; i++) { fx[i] = fx[i-1] + px[i]; if (fx[i]>1) { fx[i] = 1; sofarOK = false; } }; if (! sofarOK) { if (! confirm("Sum of probabilities is greater than 1. That's why some F(x) are truncated to 1. Is this OK ?")) { return false; } } } else { // Entering distr. function px[0] = fx[0]; for (var i = 1; i <= size; i++) { if (fx[i] < fx[i-1]) { alert("The values of the cumulative distribution are not in non-decreasing order. This has to be corrected."); return false; }; px[i] = fx[i] - fx[i-1]; } }; if ((px[0]>0) && (generMode=="Cont")) { if (! confirm("The first probability value of a continuous random variable is greater than 0. Note that the generator will assume the implicit starting point (0,0). Is this correct ?")) { return false; } }; if (fx[size]<1) { if (confirm("Sum of probabilities is less than 1, but the last value of F(x) must be equal to 1. Do you want to set it to value 1 ?")) { fx[size] = 1; } else return false; }; confirmed = true; return true; }}; distribution.prototype.confirmtable = confirmtable; //......................................................................... function generate() { with (this) { // Generates random number switch (disttype) { case "User" : var x1 = 0, f1 = 0, i = 0; // xv = array with values var x2 = xv[0], f2 = fx[0]; // fx = cumulative distribution var r = Math.random(); while (r > f2) { // search for c.d.f. not smaller than r x1 = x2; f1 = f2; x2 = xv[++i], f2 = fx[i]; }; if (generMode=="Discr") { return x2; // return the value for a discrete variable } else { return x1 + (x2-x1)*(r-f1)/(f2-f1); // Interpolation for a continuous variable } case "Exp" : return exponential(par1); case "Uni" : return uniform(par1,par2); case "Norm" : // Must be non-negative var x = normal(par1,par2); while (x<0) { x = normal(par1,par2); }; return x; default : alert("Index out of range - update."); return false; }; }}; distribution.prototype.generate = generate; //=========================================================================