Do you want to generate a number weighted to a specific direction? Want control over the minimum, maximum, direction, and amount of weight? Want to generate a number weighted to a specific number in a range?
Here's all you need:
function randomSkewedNum(strength, min, max, toMax) {
strength = toMax ? (1 / strength) : strength;
return Math.floor(Math.pow(Math.random(), strength) * (max - min + 1)) + min;
}
Here's a working example of 5 tables showing results using the code above:
function randomSkewedNum(strength, min, max, toMax) {
console.log([strength, min, max, toMax]);
strength = toMax ? (1 / strength) : strength;
return Math.floor(Math.pow(Math.random(), strength) * (max - min + 1)) + min;
}
/* Important Part ^^ */
let results;
Array.from(document.getElementsByTagName('table')).forEach((table) => {
let config = Array.from(table.previousElementSibling.getElementsByTagName('b')).map((b) => b.textContent);
results = generateResults(Number(config[3]), Number(config[0]), Number(config[1]), config[2].endsWith('x'));
displayResults(table, results);
});
function generateResults(strength, min, max, toMax) {
results = {};
for (let i = 0; i <= 10000; i++) {
let randomNum = randomSkewedNum(strength, min, max, toMax);
results[randomNum] = (results[randomNum]) ? ++results[randomNum] : 1;
}
return results;
};
function displayResults(table, results) {
Object.keys(results).sort((a, b) => a - b).forEach((result) => {
const newRow = table.getElementsByTagName('tbody')[0].insertRow(-1);
newRow.insertCell(0).innerHTML = result;
newRow.insertCell(1).innerHTML = results[result];
});
};
* { font-family: calibri; }
table {
border-collapse: collapse;
margin: 0 auto;
}
col, thead, tbody {
border: 1px solid black;
width: 10ex;
}
span {
display: block;
column-count: 3;
gap: 2ex;
}
div {
break-inside: avoid-column;
}
h6 {
text-align: center;
margin: 1em 0;
font-weight: bold;
}
<span>
<div>
<h6>Range: <b>1</b> - <b>10</b><br>Direction: to <b>Min</b><br>Strength: <b>2</b></h6>
<table>
<colgroup>
<col>
<col>
</colgroup>
<thead>
<tr>
<td>Number</td>
<td>Tally</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div>
<h6>Range: <b>-5</b> - <b>5</b><br>Direction: to <b>Min</b><br>Strength: <b>2</b></h6>
<table>
<colgroup>
<col>
<col>
</colgroup>
<thead>
<tr>
<td>Number</td>
<td>Tally</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div>
<h6>Range: <b>0</b> - <b>1000</b><br>Direction: to <b>Max</b><br>Strength: <b>1</b></h6>
<table>
<colgroup>
<col>
<col>
</colgroup>
<thead>
<tr>
<td>Number</td>
<td>Tally</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div>
<h6>Range: <b>10</b> - <b>25</b><br>Direction: to <b>Max</b><br>Strength: <b>25</b></h6>
<table>
<colgroup>
<col>
<col>
</colgroup>
<thead>
<tr>
<td>Number</td>
<td>Tally</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div>
<h6>Range: <b>0</b> - <b>5</b><br>Direction: to <b>Min</b><br>Strength: <b>5</b></h6>
<table>
<colgroup>
<col>
<col>
</colgroup>
<thead>
<tr>
<td>Number</td>
<td>Tally</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</span>
For those who want an explanation on why this code works, it's quite simple:
Here I have two numbers, 0.9 and 0.1 (notably numbers between 0 and 1 - just like what Math.random() outputs). If we square them we get:
0.9^2 = 0.81 and 0.1^2 = 0.01
Well, isn't that something, by default, squaring a random number between 0 and 1 has weight towards 0.
All, we need to do now is make use of that. But first, I'd also like to mention:
0.9^3 = 0.729 and 0.7^4 = 0.6561.
So, the bigger the "power" the more the output number is drawn to 0.
And... that's the explanation over.
From there we just upscale the randomly generated number, skewed to zero, to the range we want, and if you want to change the direction, simply use the reciprocal of the power.
0.5^(1/2) = 0.71 (3sf) and 0.9^(1/2) = 0.95 (3sf)
Notice, how now the output number is weighted to 1? Perfect.
"I want to put weight towards a specific number in a specific range!"
You can do that too, say you want weight towards "20" between the ranges 0 and 100.
Step-by-step:
- Generate a random number with Math.random().
- Check if the number is less than or greater than 0.5.
- Say, it's greater than 0.5.
- Now use the function I provided earlier to generate a skewed number between 20 and 100, towards 20.
- What if, it was lower than 0.5.
- Now use the function I provided earlier to generate a skewed number between 0 and 20, towards 20.
It's that simple.