I'm working on a way to display dice statistics like you can see at the site https://anydice.com/ , I've spent a bit looking through the source code but it's pretty thick alltogether so i decided to ask here. Basically, all i need help with is making a function that:
Lets you pick a dice with a certain amount sides, for example: 6.
Lets you pick how many times you want to roll this dice.
Returns the percentages of rolling each possible number with said dice.
I know the question might be a bit shitty, but this is kind of my last resort.
So far, I've tried finding the functions and stumbled upon this medium blog however I was wondering if it could maybe be done with percentages.
Here's a way.
// Two 6-sided dice, one 8-sided
const dice = [
[1, 2, 3, 4, 5, 6],
[1, 2, 3, 4, 5, 6],
[1, 2, 3, 4, 5, 6, 7, 8],
];
// Function to get all combinations of two arrays
function cartesianProduct(a, b) {
return a.flatMap(c => b.map(d => [c, d].flat()));
}
// Function to get sum of two numbers
function sum(a, b) { return a + b; }
// All combinations of all dice
const allPossibleRolls = dice.reduce(cartesianProduct);
// Sum for each set of rolls
const sums = allPossibleRolls.map(rolls => rolls.reduce(sum));
// Count how many times each sum appears
const counts = sums.reduce((acc, n) => Object.assign(acc, {
[n]: (acc[n] || 0) + 1
}), {});
// Convert each count into a percent by dividing by length of allPossibleRolls
const percents = Object.fromEntries(Object.entries(counts).map(
([sum, count]) => [sum, count / allPossibleRolls.length]));
Object.entries(percents).forEach(([k,v]) => {
console.log(`${k} = ${(v * 100).toFixed(5)}%`);
});
Doesn't dedupe equivalent rolls like the Medium post you linked to mentions, so the rolls [1, 2] and [2, 1] and treated as separate possibilites. Not sure if that throws off the math. But this returns the same answer as AnyDice.
Related
I just started learning Big O notation and I'm trying to understand the Big O of different functions to see which one is better.
I'm struggling to calculate the time and space complexity for the following code.
function findCommonElem(arr1, arr2) {
let result = arr1.filter(x => arr2.includes(x));
console.log(result);
}
findCommonElem(arr1, arr2);
From what I understand, common array methods like filter() usually have a big O of O(n) so in this case, it'd be O(m+n) depending on the length of each array. However, I could be super wrong.
Can someone explain, please? Thanks so much!
Bonus question: Compared to sorting the arrays then using a while loop for the same function, which one would be considered as "better"?
The above function has a time complexity of O(M * N).
But can you make this solution more efficient?
Yes. You can reduce it to O(M + N).
TLDR: Use a hash table to achieve linear time complexity O(M + N).
Approach 1
Check every elements of array 1 with every element of array 2. (This is the approach you're using.)
function findCommonElem(arr1, arr2) {
return arr1.filter(x => arr2.includes(x));
}
const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const arr2 = [2, 4, 6, 8, 10, 12, 14, 16, 20];
console.log(findCommonElem(arr1, arr2)); // [2, 4, 6, 8, 10];
Time complexity = O(M * N)
Every element from array 1 is checked with every element of array 2 at the worst case. So, it's M * N.
Space compexity = O(M) or O(N)
At most, all of the elements from one of the either arrays could be in the intersection.
Approach 2
Use a hash map to linearize the nested loop. First, populate the hash map with array 1 elements. Then check through the array 2 using the map to find the intersection.
function findCommonElem(arr1, arr2) {
const map = new Map();
arr1.forEach(item => map.set(item, true));
return arr2.filter(item => map.has(item));
}
const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const arr2 = [2, 4, 6, 8, 10, 12, 14, 16, 20];
console.log(findCommonElem(arr1, arr2)); // [2, 4, 6, 8, 10];
The above function returns the same output. But here's the difference - the nested loop is reduced to two linear loops for the arrays. This means both arrays are traversed only once.
Time complexity = O(M + N)
Array 1 is traversed once (M elements).
Array 2 is traversed once (N elements).
Checking if the map contains the element with map.has() takes constant time O(1).
Total run time = M + N
Space compexity = O(M) or O(N)
Space complexity is still the same here, because space needed for the new hash map is either O(M) or O(N). And our intermediate array takes either O(M) or O(N). Which is still the same.
Bonus: Don't know much about how hash maps work internally? Watch this.
Approach 3
Use set instead of map. The set data structure is best for this use case as you don't need the mapped value (the true value) in approach 2.
function findCommonElem(arr1, arr2) {
const set = new Set(arr1);
return arr2.filter(item => set.has(item));
}
const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const arr2 = [2, 4, 6, 8, 10, 12, 14, 16, 20];
console.log(findCommonElem(arr1, arr2)); // [2, 4, 6, 8, 10];
This uses relatively lesser space but the algorithmic complexity of TC and SC are still same.
Time complexity = O(M + N)
Space compexity = O(M) or O(N)
Thanks to Nick Parsons for pointing this out.
let's say that arr1.length is n and arr2.length is m.
so the filter function run the lambda function you wrote for each item in arr1. The lambda function checks if an item is in the arr2 and in the worst case where it didn't find it the function ran on all the array so m times.
therefore arr1.filter(x => arr2.includes(x)) run at the worst case O(n*m).
as for the space complexity, the filter function creates a new array and in the worst case that array size is as big as the original so the space complexity is O(n)
As it correctly mentioned, big O here would be equal to O(n*m), here is an explanation for this:
arr1.filter complexity is O(n)
arr2.includes complexity is O(n) as well
However for each iteration in .filter you execute each time arr2.includes, which leads to O(n) * O(n) = O(n * m)
You may increase performance if you replace for example arr2 with JS Set. JS Set.has complexity is O(1), so using Set instead of arr2 should help you to achieve O(n) complexity.
I believe I cannot answer on sorting question, cause I haven't understood clearly what you mean.
Find the correct passcode in the array and we'll do the rest. We can't disclose more information on this one, sorry.
Each entry in the first array represents a passcode
- Find the passcode that has no odd digits.
- For each passcode, show us the amount of even digits.
- If it has no odd digits, show us that you've found it and increase the number of terminals by one.
var passcodes = [
[1, 4, 4, 1],
[1, 2, 3, 1],
[2, 6, 0, 8],
[5, 5, 5, 5],
[4, 3, 4, 3],
];
so, i've tried almost everything i could think of. modulo, function, for loop and i can't seem to get it. i'm a beginner and this is an important exercise i have to do. but what do i do? it asks for the amount of even digits in each passcode, so i have to get the array within the array and then code something that i don't know to find even values. i'm stuck
Your question is not really suitable for StackOverflow, you should at least try to write something and see how far you get.
Anyhow, you seem to want to iterate over the elements in passcodes to find the array with no odd numbers.
The first task is to how to determine if a number is even. That is as simple as looking for the remainder from modulus 2. If the remainder is zero, then the number is even, otherwise it's odd.
So a simple test is:
var isEven;
if (x % 2 == 0) {
isEven = true;
} else {
isEven = false;
}
Since 0 type converts to false, and the not (!) operator reverses the truthiness of values and converts the result to boolean, the above can be written:
var isEven = !(x % 2);
There are many ways to iterate over an array, if your task was just to find the element with no odd numbers, I'd use Array.prototype.every, which returns as soon as the test returns false, or Array.prototype.some, which returns as soon as the test returns true.
However, in this case you want to count the number of even numbers in each element and find the first with all even numbers. One way is to iterate over the array and write out the number of even numbers in the element, and also note if its all even numbers. You haven't said what the output is expected to be, so I've just made a guess.
var passcodes = [
[1, 4, 4, 1],
[1, 2, 3, 1],
[2, 6, 0, 8],
[5, 5, 5, 5],
[4, 3, 4, 3], // this last comma affects the array length in some browsers, remove it
];
// Set flag for first element with all even numbers
var foundFirst = false;
// Iterate over each element in passcodes
passcodes.forEach(function(code) {
// Count of even numbers in current array
var evenCount = 0;
// Test each element of code array and increment count if even
code.forEach(function(num) {
if (!(num % 2)) ++evenCount;
});
// If all elements are even and haven't found first yet, write out elements
if (code.length == evenCount && !foundFirst) {
console.log('Passcode (first all even): ' + code.join());
// Set flag to remember have found first all even array
foundFirst = true;
}
// Write count of even numbers in this array
console.log('Even number count: ' + evenCount + ' of ' + code.length + ' numbers.');
});
I have no idea what you meant ...but it does all what i could understand from your question. Hope it will help :)
var passcodes = [
[1, 4, 4, 1],
[1, 2, 3, 1],
[2, 6, 0, 8],
[5, 5, 5, 5],
[4, 3, 4, 3],
];
var aPassCode;
while(aPassCode = passcodes.shift()){
for(var i=0,evenCount=0,totalCount=aPassCode.length;i<totalCount;i++){
if(aPassCode[i] % 2 == 0)evenCount++;
}
if(evenCount == totalCount){
console.log('all digits even here: ' + aPassCode.join(','));
}else{
console.log(aPassCode.join(',') + ' contains ' + evenCount + ' even digits.');
}
}
I'm trying to understand the following solution for finding the largest adjacent product in any given array.
Example:
For inputArray = [3, 6, -2, -5, 7, 3], the output should be
adjacentElementsProduct(inputArray) = 21.
7 and 3 produce the largest product.
Possible solution in JS:
function adjacentElementsProduct(arr) {
return Math.max(...arr.slice(1).map((x,i)=>[x*arr[i]]))
}
I am having a hard time understanding two things:
What do the three dots exactly do and how does this get passed into the function? Is there any way to write this in a more understandable way? I know that is the "spread syntax" feature in ES6, but still don't understand completely.
Why do we insert "1" as argument to slice? My first though was to input "0", because we want to start at the start, then loop through everything, and see which adjacent product is the largest.
I'd appreciate any advice, links and explanations.
Thanks.
Cheers!
1. What do the three dots exactly do and how does this get passed into the function? Is there any way to write this in a more understandable way? I know that is some kind of "spread" feature in ES6, but still don't understand completely.
The Math#max needs a list of numbers as parameters, and map produces an array. The spread syntax is used to convert an array to be expanded to a list of parameters.
const arr = [1, 2, 3];
console.log('max on array', Math.max(arr));
console.log('max on list of parameters', Math.max(...arr));
In this case you can use Function#apply to convert the array to a list of parameters. I find it less readable, however.
const arr = [1, 2, 3];
console.log(Math.max.apply(Math, arr));
2. Why do we insert "1" as argument to slice? My first though was to input "0", because we want to start at the start, then loop through everything, and see which adjacent product is the largest.
Lets break down the iteration order of the 2 arrays.
[3, 6, -2, -5, 7, 3] // inputArray
[6, -2, -5, 7, 3] // inputArray.slice(1)
Now on each iteration of inputArray.slice(1):
x: 6, i = 0, arr[0] = 3
x: -2, i = 1, arr[1] = 6
x: -5, i = 2, arr[2] = -2
Since the inputArray.slice(1) array starts from the 2nd element of the inputArray, the index (i) points to the 1st element of the inputArray. And the result is an array of products of 2 adjacent numbers.
var biggestProduct = inputArray[0] * inputArray[1];
for (i=0; i<inputArray.length-1 ; ++i)
{
console.log(biggestProduct)
if ((inputArray[i] * inputArray[i+1] ) > biggestProduct)
{
biggestProduct = inputArray[i] * inputArray[i+1]
}
}
return biggestProduct;
Note: I've declared a variable that consists of 2 input arrays with index number then starts a for loop that indicates input array with his index number, so by that he will go throw all the index number of the array (one of them raised by one so that they won't be at the same value). and at the end of the code, you have the if statement.
You may simply do as follows;
function getNeigboringMaxProduct([x,...xs], r = -Infinity){
var p = x * xs[0];
return xs.length ? getNeigboringMaxProduct(xs, p > r ? p : r)
: r;
}
var arr = [3, 6, -2, -5, 7, 3],
res = getNeigboringMaxProduct(arr);
console.log(res);
I was trying to figure out which is the fastest way to do the following task:
Write a function that accepts two arrays as arguments - each of which is a sorted, strictly ascending array of integers, and returns a new strictly ascending array of integers which contains all values from both of the input arrays.
Eg. merging [1, 2, 3, 5, 7] and [1, 4, 6, 7, 8] should return [1, 2, 3, 4, 5, 6, 7, 8].
Since I don't have formal programming education, I fint algorithms and complexity a bit alien :) I've came with 2 solutions, but I'm not sure which one is faster.
solution 1 (it actually uses the first array instead of making a new one):
function mergeSortedArrays(a, b) {
for (let i = 0, bLen = b.length; i < bLen; i++) {
if (!a.includes(b[i])) {
a.push(b[i])
}
}
console.log(a.sort());
}
const foo = [1, 2, 3, 5, 7],
bar = [1, 4, 6, 7, 8];
mergeSortedArrays(foo, bar);
and solution 2:
function mergeSortedArrays(a, b) {
let mergedAndSorted = [];
while(a.length || b.length) {
if (typeof a[0] === 'undefined') {
mergedAndSorted.push(b[0]);
b.splice(0,1);
} else if (a[0] > b[0]) {
mergedAndSorted.push(b[0]);
b.splice(0,1);
} else if (a[0] < b[0]) {
mergedAndSorted.push(a[0]);
a.splice(0,1);
} else {
mergedAndSorted.push(a[0]);
a.splice(0,1);
b.splice(0,1);
}
}
console.log(mergedAndSorted);
}
const foo = [1, 2, 3, 5, 7],
bar = [1, 4, 6, 7, 8];
mergeSortedArrays(foo, bar);
Can someone help me with time complexity of both solutions? Also, is there another, faster solution? Should I be using reduce()?
Your first function modifying passed argument and this is bad practice.
You can do it in the following way:
function mergeSortedArrays(a, b) {
return a.concat(b.filter(el => !a.includes(el))).sort();
}
The complexity also depends on the methods that you use in your code.
1) An algorithm commonly used for sorting is Quicksort (introsort as a variation of quicksort).
It has O(n log n) complexity however the worst case may still be O(n^2) in case the input is already sorted. So your solution has O( length(b) + length(a)+length(b) log (length(a)+length(b)) ) runtime complexity.
2) According to this question on the complexity of splice() the javascript function needs O(n) steps at worst (copying all elements to the new array of size n+1). So your second solution takes length of array b multiplied by n steps needed to copy the elements during splice plus the time to push().
For a good solution that works in linear time O(n+m) refer to this Java example and port it (i.e. create an array of size length(a) + length(b) and step via the indeces as shown). Or check out the very tight and even a littler faster implementation below of the answer.
If we get something like
array=[5,5,5,5,3,2];
return Math.max.Apply(Math,array);
How do I get it to return the numbers from first to last if such a case occurs.
To answer the question in the title:
what does max() function do in javascript if array has several equally
large numbers
The answer is, nothing. Math.max() doesn't act on arrays.
You can pass an array by spreading the items as arguments to max():
Math.max(...[1,2,3]) // 3
Or as you've seen, with apply():
Math.max.apply(Math, [1,2,3]) // 3
If the question is more:
What does Math.max() do when more than one of the same maximum number is given?
The answer is, it returns that number:
const a = [5, 5, 5, 5, 3, 2]
const max = Math.max(...a)
console.log(max) // 5
This question is confusing:
How do I get it to return the numbers from first to last if such a case occurs.
You want it to return a sorted array? From [5, 5, 5, 5, 3, 2] to [2, 3, 5, 5, 5, 5]?
a.sort() // [2, 3, 5, 5, 5, 5]
You want dupes removed? From [5, 5, 5, 5, 3, 2] to [2, 3, 5]?
Array.from(new Set(a)) // [2, 3, 5]
Could you clarify your question?
The best way to do this is the following:
var a = [5,5,5,5,3,2];
var largest = Math.max.apply(null,a)
var filtered = a.filter(function(item) {
item === largest
});
Where filtered will have contain all the largest elements.
In #Clarkie's example, he's calling Math.max more frequently than needed.
In both Dan and Clarkie's example they're capitalizing Apply which is incorrect, the correct function to call is Math.max.apply and Math need not be passed in as the first argument.
See the following for a working example:
https://jsfiddle.net/fx5ut2mm/
Modifying #Clarkie's very nice idea. We can boil it down to...
var a = [5,5,5,5,3,2],
m = Math.max(...a),
f = a.filter(e => e == m);
document.write("<pre>" + JSON.stringify(f) + "</pre>");