I'm having a variable number of dice and dice types (eg. 4-side, 6-sided, etc), but dice types are never mixed. I need to calculate the probability of a certain outcome using any variation of dice and sides. For example, given 3 six-sided dice (which we'll write as 3d6), the probability of rolling 3 is 0.46% (100 / 6^3 = 0.46).
Probability = Number of desired outcomes ÷ Number of possible outcomes
Number of possible outcomes: Number of sides to the power of the number of dice, eg. 6^3.
Number of desired outcomes: ?
The application for this is that the dice formula can have modifier, ex. 3d6-5, and I'd like to calculate the chance that the outcome is zero or lower.
Thus, I was thinking that by adding up the percentages of to sum the total chance. Example (using anydice.com), for (3d6)-5, I'd add the chance for rolls of 3 (0.46%),4 (1.39%), 5 (2.78%), to come to the conclusion that there is a 4.63% chance of rolling equal to or lower than 0.
I'm looking for a somewhat efficient way to do this in Javascript somehow.
EDIT: Order matters! a roll of 6, 1 and 2 is not the same as 1, 2 and 6. This is not a combinatorics problem, as presented in the Medium article suggested by Barmar.
EDIT: Comments ask (rightfully so) for code. As I don't have a hypothesis on how to calculate it, I don't have much to that end, but this is what I do have:
function dieAvg(sides) {
return ((sides+1)/2);
}
// dice = number of dice
// die = sides, eg. 6 for a square dice
// mod = a modifier, positive or negative to apply to the result
function getDiceStats(dice, die, mod) {
let avg = (dieAvg(die)*dice) + mod;
let min = dice + mod;
let max = die*dice) + mod;
let zeroChance = 0; // TODO!
return [min,max,avg, zeroChance];
}
Somewhat related question (java): Calculate the number of ways to roll a certain number
Related
I am curious how Stake.com managed to create the game "Limbo" where the odds of a multiplier happening is specific to the probability they've calculated. Here's the game : https://stake.com/casino/games/limbo
For example :
Multiplier -> x2
Probability -> 49.5% chance.
What it means is you have a 49.5% chance of winning because those are the odds that the multiplier will actually hit a number above x2.
If you set the multiplier all the way up to x1,000,000. You have a 0.00099% chance of actually hitting 1,000,000.
It's not a project I'm working on but I'm just extremely curious how we could achieve this.
Example:
Math.floor(Math.random()*1000000)
is not as random as we think, since Math.random() generates a number between 0-1. When paired with a huge multiplier like 1,000,000. We would actually generate a 6-figure number most of the time and it's not as random as we thought.
I've read that we have to convert it into a power law distribution but I'm not sure how it works. Would love to have more material to read up on how it works.
It sounds like you need to define some function that gives the probability of winning for a given multiplier N. These probabilities don't have to add up to 1, because they are not part of the same random variable; there is a unique random variable for each N chosen and two events, win or lose; we can subscript them as win(N) and lose(N). We really only need to define win(N) since lose(N) = 1 - win(N).
Something like an exponential functional would make sense here. Consider win(N) = 2^(1 - N). Then we get the following probabilities of winning:
n win(n)
1 1
2 1/2
3 1/4
4 1/8
etc
Or we could use just an inverse function: win(N) = 1/N
n win(n)
1 1
2 1/2
3 1/3
...
Then to actually see whether you win or lose for a given N, just choose a random number in some range - [0.0, 1.0) works fine for this purpose - and see whether that number is less than the win(N). If so, it's a win, of not, it's a loss.
Yes, technically speaking, it is probably true that the floating point numbers are not really uniformly distributed over [0, 1) when calling standard library functions. If you really need that level of precision then you have a much harder problem. But, for a game, regular rand() type functions should be plenty uniform for your purposes.
I need to develop an algorithm that randomly selects values within user-specified intervals. Furthermore, these values need to be separated by a minimum user-defined distance. In my case the values and intervals are times, but this may not be important for the development of a general algorithm.
For example: A user may define three time intervals (0900-1200, 1200-1500; 1500-1800) upon which 3 values (1 per interval) are to be selected. The user may also say they want the values to be separated by at least 30 minutes. Thus, values cannot be 1159, 1201, 1530 because the first two elements are separated by only 2 minutes.
A few hundred (however many I am able to give) points will be awarded to the most efficient algorithm. The answer can be language agnostic, but answers either in pseudocode or JavaScript are preferred.
Note:
The number of intervals, and the length of each interval, are completely determined by the user.
The distance between two randomly selected points is also completely determined by the user (but must be less than the length of the next interval)
The user-defined intervals will not overlap
There may be gaps between the user-defined intervals (e.g., 0900-1200, 1500-1800, 2000-2300)
I already have the following two algorithms and am hoping to find something more computationally efficient:
Randomly select value in Interval #1. If this value is less than user-specified distance from the beginning of Interval #2, adjust the beginning of Interval #2 prior to randomly selecting a value from Interval #2. Repeat for all intervals.
Randomly select values from all intervals. Loop through array of selected values and determine if they are separated by user-defined minimum distance. If not (i.e., values are too close), randomly select new values. Repeat until valid array.
This works for me, and I'm currently not able to make it "more efficient":
function random(intervals, gap = 1){
if(!intervals.length) return [];
// ensure the ordering of the groups
intervals = intervals.sort((a,b) => a[0] - b[0])
// check for distance, init to a value that can't exist
let res = []
for(let i = 0; i < intervals.length; i++){
let [min, max] = intervals[i]
// check if can exist a possible number
if(i < intervals.length - 1 && min + gap > intervals[i+1][1]){
throw new Error("invalid ranges and gap")
}
// if we can't create a number in the current section, try to generate another number from the previous
if( i > 0 && res[i-1] + gap > max){
// reset the max value for the previous interval to force the number to be smaller
intervals[i-1][1] = res[i-1] - 1
res.pop()
i-=2
}
else {
// set as min the lower between the min of the interval and the previous number generated + gap
if( i > 0 ){
min = Math.max(res[i-1] + gap , min)
}
// usual formula to get a random number in a specific interval
res.push(Math.round(Math.random() * (max - min) + min))
}
}
return res
}
console.log(random([
[0900, 1200],
[1200, 1500],
[1500, 1800],
], 400))
this works like:
generate the first number ()
check if can generate second number (for the gap rule)
- if i can, generate it and go back to point 2 (but with the third number)
- if i can't, I se the max of the previous interval to the generated number, and make it generate it again (so that it generates a lower number)
I can't figure out what's the complexity, since there are random number involved, but might happen that with 100 intervals, at the generation of the 100th random number, you see that you can't, and so in the worst case this might go back generating everything from the first one.
However, every time it goes back, it shrinks the range of the intervals, so it will converge to a solution if exists
This seems to do the job. For explanations see comments in the code ...
Be aware, that this code does not do any checks of your conditions, ie non overlapping intervals and intervals are big enough to allow the mindist to be fulfilled. If the conditions are not met, it may generate erroneous results.
This algorithm allows the minimum distance between two values to be defined with each interval separately.
Also be aware, that an interval limit like 900 in this algorithm does not mean 9:00 o'clock, but just the numeric value of 900. If you want the intervals to represent times, you have to represent them as, for instance, minutes since midnight. Ie 9:00 will become 540, 12:00 will become 720 and 15:00 will become 900.
EDIT
With the current edit it also supports wrap-overs at midnight (Although it does not support intervals or minimum distances of more than a whole day)
//these are the values entered by the user
//as intervals are non overlapping I interpret
//an interval [100, 300, ...] as 100 <= x < 300
//ie the upper limit is not part of that interval
//the third value in each interval is the minimum
//distance from the last value, ie [500, 900, 200]
//means a value between 500 and 900 and it must be
//at least 200 away from the last value
//if the upper limit of an interval is less than the lower limit
//this means a wrap-around at midnight.
//the minimin distance in the first interval is obviously 0
let intervals = [
[100, 300, 0],
[500, 900, 200],
[900, 560, 500]
]
//the total upper limit of an interval (for instance number of minutes in a day)
//lenght of a day. if you don't need wrap-arounds set to
//Number.MAX_SAFE_INTEGER
let upperlimit = 1440;
//generates a random value x with min <= x < max
function rand(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
//holds all generated values
let vals = [];
let val = 0;
//Iterate over all intervals, to generate one value
//from each interval
for (let iv of intervals) {
//the next random must be greater than the beginning of the interval
//and if the last value is within range of mindist, also greater than
//lastval + mindist
let min = Math.max(val + iv[2], iv[0]);
//the next random must be less than the end of the interval
//if the end of the interval is less then current min
//there is a wrap-around at midnight, thus extend the max
let max = iv[1] < min ? iv[1] + upperlimit : iv[1];
//generate the next val. if it's greater than upperlimit
//it's on the next day, thus remove a whole day
val = rand(min, max);
if (val > upperlimit) val -= upperlimit;
vals.push(val);
}
console.log(vals)
As you may notice, this is more or less an implementation of your proposal #1 but I don't see any way of making this more "computationally efficient" than that. You can't get around selecting one value from each interval, and the most efficent way of always generating a valid number is to adjust the lower limit of the interval, if neccessary.
Of course, with this approach, the selection of next number is always limited by the selection of the previous. Especially if the minimum distance between two numbers is near the length of the interval, and the previous number selected was rather at the upper limit of its interval.
This can simply be done by separating the intervals by required many minutes. However there might be edge cases like a given interval being shorter than a seperation or even worse two consequent intervals being shorter than the separation in which case you can safely throw an error. i.e. had in [[900,1200],[1200,1500]] case 1500 - 900 < 30 been. So you best check this case per consequent tuples and throw an error if they don't satisfy before trying any further.
Then it gets a little hairy. I mean probabilistically. A naive approach would chose a random value among [900,1200] and depending on the result would add 30 to it and accordingly limit the bottom boundary of the second tuple. Say if the random number chosen among [900,1200] turns out to be 1190 then we will force the second random number to be chosen among [1220,1500]. This makes second random choice dependent on the outcome of the first choice and as far as I remember from probability lessons this is no good. I believe we have to find all possible borders and make a random choice among them and then make two safe random choices one from each range.
Another point to consider is, this might be a long list of tuples to start with. So we should care about not limiting the second tuple in each turn since it will be the first tuple on the next turn and we would like to have it as wide as possible. So perhaps getting the minimum possible value from the first range (limitting the first range as much as possible) may turn out to be more productive than random tries which might (most possibly) yield a problem in further steps.
I can give you the code but since you haven't showed any tries you have to settle with this rod to go and fish yourself.
I was doing this leetcode question: https://leetcode.com/problems/house-robber/
The question is
You are a professional robber planning to rob houses along a street.
Each house has a certain amount of money stashed, the only constraint
stopping you from robbing each of them is that adjacent houses have
security system connected and it will automatically contact the police
if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money
of each house, determine the maximum amount of money you can rob
tonight without alerting the police.
With following example
Input: nums = [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.
I wrote the following code for this
/**
* #param {number[]} nums
* #return {number}
*/
var rob = function(nums) {
if (nums.length === 0) return 0
let output = i = 0
while (i < nums.length) {
const currentHouse = nums[i]
i = i + 2
output += currentHouse
}
return output
};
console.log(rob([1, 2]));
console.log(rob([1, 2, 3, 1]));
but it fails for this particular
Input:
[1,2]
Output:
1
Expected:
2
I am not sure why the output should be 2 for the above test?
Your algorithm just takes the houses at even indexes without taking into account that you have different options.
For instance when there are 5 houses (numbered 0 to 4) you can consider the following alternatives:
rob house 0, 2 and 4
rob house 0 and 3
rob house 1 and 3
rob house 1 and 4
Each of these could be the optimal strategy for the robber... it all depends on the amounts of money in the houses. For instance:
When input is [1,1,1,1,1] then strategy 1 is optimal and your program produces the correct output (3)
When input is [2,1,1,3,1] then strategy 2 is optimal (5), while your program will output 4.
When input is [1,2,1,3,1] then strategy 3 is optimal (5), while your program will output 3.
When input is [1,3,1,1,2] then strategy 4 is optimal (5), while your program will output 4.
Once you realise you need to look at different patterns, you can see that sometimes you need skip a house even if it was not adjacent to a previously robbed house. It is also not guaranteed that robbing the first house is optimal. On the other hand, it is never optimal to skip 3 houses in row, as you then might as well rob the middle of those, as it is not adjacent to another robbed house.
You can have an algorithm that progressively adds a house to the problem, and keeps track what was the best for the two previous sub problems (so with 2 or 1 fewer houses).
We can then look at the situation where we rob the newly added house or not. If we rob it, we should add the money to the money we got from the problem with 2 fewer houses. If we don't rob it, we should just copy what we got from the problem with 1 fewer houses.
This is a so-called bottom up approach:
function rob(nums) {
// Represent the optimal amount we get from a problem with fewer houses:
let sumTwoHousesLess = 0;
let sumOneHouseLess = 0;
let sum = 0; // The amount for a problem without any houses.
for (let money of nums) { // Add a house to the problem in each iteration
// See what is best: rob this house or not:
sum = Math.max(money + sumTwoHousesLess, sumOneHouseLess);
// Shift the information we have, as we are about to go to the next house
sumTwoHousesLess = sumOneHouseLess;
sumOneHouseLess = sum;
}
return sum;
}
console.log(rob([1,2])); // 2
console.log(rob([1,1,1,1,1])); // 3
console.log(rob([2,1,1,3,1])); // 5
console.log(rob([1,2,1,3,1])); // 5
console.log(rob([1,3,1,1,2])); // 5
I am stuck on how my code will not be accepted by freeCodeCamp's auto-grader. The objective was to create a function without any parameters that will generate a random number using Math.random(). Then we would have to multiply the randomly generated number by 10. After multiplying the randomly generated by 10, I would have to use Math.floor() to round it up or down.
Link to challenge: https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-javascript/generate-random-whole-numbers-with-javascript
These are the objectives given to me, quoted directly from freeCodeCamp's challenge:
1.) The result of randomWholeNum should be a whole number.
2.) You should use Math.random to generate a random number.
3.) You should have multiplied the result of Math.random by 10 to make it a number that is between zero and nine.
4.) You should use Math.floor to remove the decimal part of the number.
Here is my code:
function randomWholeNum() {
var x = Math.random();
var z = x * 10;
var y = Math.floor(z);
return y;
}
As you can see, I used multiple variables to complete this task. x would generate the random number, z would store the random number multiplied by 10, y would round z, resulting in all objectives passed. However, when running the code, all objectives were ticked except for Number 3. I don't understand what went wrong, and after looking at the answer for the challenge, which is:
function randomWholeNum() {
// Only change code below this line.
return Math.floor(Math.random() * 10);
}
I don't understand why the code I wrote doesn't produce the same result of freeCodeCamp's own.
I’m having problems generating normally distributed random numbers (mu=0 sigma=1)
using JavaScript.
I’ve tried Box-Muller's method and ziggurat, but the mean of the generated series of numbers comes out as 0.0015 or -0.0018 — very far from zero!! Over 500,000 randomly generated numbers this is a big issue. It should be close to zero, something like 0.000000000001.
I cannot figure out whether it’s a method problem, or whether JavaScript’s built-in Math.random() generates not exactly uniformly distributed numbers.
Has someone found similar problems?
Here you can find the ziggurat function:
http://www.filosophy.org/post/35/normaldistributed_random_values_in_javascript_using_the_ziggurat_algorithm/
And below is the code for the Box-Muller:
function rnd_bmt() {
var x = 0, y = 0, rds, c;
// Get two random numbers from -1 to 1.
// If the radius is zero or greater than 1, throw them out and pick two
// new ones. Rejection sampling throws away about 20% of the pairs.
do {
x = Math.random()*2-1;
y = Math.random()*2-1;
rds = x*x + y*y;
}
while (rds === 0 || rds > 1)
// This magic is the Box-Muller Transform
c = Math.sqrt(-2*Math.log(rds)/rds);
// It always creates a pair of numbers. I'll return them in an array.
// This function is quite efficient so don't be afraid to throw one away
// if you don't need both.
return [x*c, y*c];
}
If you generate n independent normal random variables, the standard deviation of the mean will be sigma / sqrt(n).
In your case n = 500000 and sigma = 1 so the standard error of the mean is approximately 1 / 707 = 0.0014. The 95% confidence interval, given 0 mean, would be around twice this or (-0.0028, 0.0028). Your sample means are well within this range.
Your expectation of obtaining 0.000000000001 (1e-12) is not mathematically grounded. To get within that range of accuracy, you would need to generate about 10^24 samples. At 10,000 samples per second that would still take 3 quadrillon years to do...this is precisely why it's good to avoid computing things by simulation if possible.
On the other hand, your algorithm does seem to be implemented correctly :)