I am trying to make a function in javascript that returns an array from range(start,end) and im supposed to make an optional argument that defaults to one when it is undefined. I can get the function to work when I provide all the arguments but returns an empty array when I only pass two arguments. Here is the question:
Write a range function that takes two arguments, start and end, and returns an array containing all the numbers from start up to (and including) end.
Next, write a sum function that takes an array of numbers and returns the sum of these numbers. Run the previous program and see whether it does indeed return 55.
As a bonus assignment, modify your range function to take an optional third argument that indicates the “step” value used to build up the array. If no step is given, the array elements go up by increments of one, corresponding to the old behavior. The function call range(1, 10, 2) should return [1, 3, 5, 7, 9]. Make sure it also works with negative step values so that range(5, 2, -1) produces [5, 4, 3, 2].
And here is my code:
function range(start, end, increment){
var array = [];
var current = start;
var counter;
if (increment == undefined){
counter = 1;
}
else {
counter = increment;
}
if (increment > 0){
while(current <= end){
array.push(current);
current += counter;
}
}
else if (increment < 0){
while(current >= end){
array.push(current);
current += counter;
}
}
return array;
}
can someone explain why its breaking? I know some c# and I used to being able to jump into the debugger in visual studio when something goes wrong unlike javascript.
A very simple unidirectional (ascending), inclusive range – goes from x to y incrementing by 1 each time.
// range :: (Int, Int) -> [Int]
const range = (x,y) =>
x > y ? [] : [x, ...range(x + 1, y)];
console.log(range(1,4)); // [1,2,3,4]
console.log(range(3,3)); // [3]
console.log(range(6,3)); // []
A slight adaptation that supports bidirectional (ascending or descending) range – still increments or decrements by 1
// range :: (Int, Int) -> [Int]
const range = (x,y) => {
if (x > y)
return range(y,x).reverse();
else
return x === y ? [y] : [x, ...range(x + 1, y)];
}
console.log(range(1,4)); // [1,2,3,4]
console.log(range(3,3)); // [3]
console.log(range(6,3)); // [6,5,4,3]
Another adaptation that uses higher-order functions for more control over the range – this effectively gives you the stepping/incrementing behaviour some of you are looking for – tho this is more powerful because it lets you use a function, t, to choose the next value.
const gte = x => y => y >= x;
const lte = x => y => y <= x;
const add = x => y => y + x;
const sub = x => y => y - x;
// range :: (Int, (Int -> Bool), (Int -> Int)) -> [Int]
const range = (x, p, t) => {
if (p(x))
return [x, ...range(t(x), p, t)];
else
return [];
};
console.log(range(2, lte(8), add(2))); // [2,4,6,8]
console.log(range(9, gte(0), sub(3))); // [9,6,3,0]
console.log(range(9, gte(0), sub(5))); // [9, 4]
// very power. wow.
const double = x => x + x;
console.log(range(2, lte(50), double)); // [2,4,8,16,32]
This function has the same risks inherent with for and while – it's up to you to make sure you don't put it into an infinite loop.
functional overload
Warning: Esoteric, impractical functionals ahead. The following information is provided for your academic pleasure only.
The range function also happens to be one of my favourite demonstrations of the Y combinator. I'll show you two examples here.
naïve range
const U = f => f (f);
const Y = U (h => f => f (x => h (h) (f) (x)));
const range = Y (f => acc => x => y =>
x > y ? acc : f ([...acc, x]) (x + 1) (y)
) ([]);
console.log(range (3) (6)); // [3,4,5,6]
console.log(range (6) (6)); // [6]
console.log(range (9) (6)); // []
and the higher-order range
const U = f => f (f);
const Y = U (h => f => f (x => h (h) (f) (x)));
const lt = x => y => y < x;
const gt = x => y => y > x;
const add1 = x => x + 1;
const sub1 = x => x - 1;
const range = Y (f => acc => x => p => t =>
p(x) ? f ([...acc, x]) (t(x)) (p) (t) : acc
) ([]);
console.log(range (3) (lt(6)) (add1)); // [3,4,5]
console.log(range (6) (lt(6)) (add1)); // []
console.log(range (9) (gt(6)) (sub1)); // [9,8,7]
What a thing of beauty that is.
You could simplify the code a bit and use the increment variable for incrementing. But before, I suggest to test if the value is falsy (0, null, undefined, etc) and assign then 1 to it.
Not implemented: check if start and end is appropriate.
function range(start, end, increment) {
var array = [];
var current = start;
increment = increment || 1;
if (increment > 0) {
while (current <= end) {
array.push(current);
current += increment;
}
} else {
while (current >= end) {
array.push(current);
current += increment;
}
}
return array;
}
console.log(range(1, 3, 0));
console.log(range(2, 5));
console.log(range(1, 9, 1));
console.log(range(5, 2, -1));
First you check if increment is undefined and set counter accordingly, but later you check if (increment > 0){ again. While it is undefined none of your cases matches, so nothing happens.
Change your checks to this:
if (counter > 0){
// ...
}
else if (counter < 0){
// ...
}
Very compact range function that handles float and negative numbers:
const range = (lower,upper,step)=>{
return Array.from(new Array(Math.floor(upper/step-lower/step)+1),(_,i)=>lower/step+i).map(x=>x*step)
}
For example you can use it like:
range(10,30,3) // [10, 13, 16, 19, 22, 25, 28]
range(0,0.5,0.01) // [0, 0.01, 0.02, ... , 0.48, 0.49, 0.5]
range(1,10,2) // [1, 3, 5, 7, 9]
range(-5,10,0.5) // [-5, -4.5, -4, ... , 1, 1.5, 2]
range(5,2,-0.5) // [5, 4.5, 4, 3.5, 3, 2.5, 2]
Here a more understandable version:
const range = (lower, upper, step) => {
end = upper / step // Upper bound
start = lower / step // Lower bound
n = Math.floor(end - start) + 1 // Size that includes upper bound as well
zeros_arr = Array(n).fill(0) // Init array with n zeros
unscaled_arr = zeros_arr.map((_, i) => (start + i)) // Setting each zero to the upper bound + the index
range_arr = unscaled_arr.map(x => (x * step)) // Scaling every numbers with the step
return range_arr
}
The given answers are great. I just wanted to give you an idea of how a more functional approach could solve the task:
// auxiliary functions:
const append = (x, xs) => xs.concat([x]);
const prepend = (x, xs) => [x].concat(xs);
// main function
const range = (x, y, step, acc = [], op = append) =>
step && step < 0
? range(y, x, -step, acc, prepend)
: step && x <= y
? range(x + step, y, step, op(x, acc), op) // tail call
: acc;
console.log(range(1,5,1)); // [1,2,3,4,5]
console.log(range(1,5,2)); // [1,3,5]
console.log(range(1,5,6)); // [1]
console.log(range(5,1,1)); // []
console.log(range(1,5,0)); // []
console.log(range(5,1,-1)); // [5,4,3,2,1]
console.log(range(5,1,-2)); // [5,3,1]
console.log(range(5,1,-6)); // [1]
console.log(range(1,5,-1)); // []
Algorithm:
acc = [] and op = append default parameter values (are taken if omitted during the function invocation)
step && step < 0 short circuits if step is zero, otherwise checks if step is negative
range(y, x, -step, acc, prepend) is called when step is negative and converts range's parameterization so that step can be positive (note that -step is equivalent with -(-1), which is evaluated to 1)
range(x + step, y, step, op(x, acc), op) recursive case that means, the function calls itself (notice that op can be either append or prepend depending on the initial sign of step, that x is increased by step and appended/prepended to acc)
acc base case that stops the recursion and returns the accumulated array
I solved this problem as part of the eloquent javascript course. Based on the problem statement I chose default parameters for the increments and used a while loop to include the second argument.
function range(start, stop, increment=1)
{
let range_arr = [];
if(start < stop)
{
while( start <= stop)
{
range_arr.push(start);
start += increment;
}
}
else{
while( start >= stop)
{
range_arr.push(start);
start += increment;
}
}
return range_arr;
}
I decided to use the for loop to create the increments as follows.
function range(start,end) {
let array = [];
for (let counter = 0; counter < end; counter++) {
array.push(start);
start += 1;
}
return array;
}
console.log(range(1,10)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
I've using the snippet for range, with help of iterative callback theory.
That repeat the same function function with respected range attribute i.e 10-100. also 10-100 with 2 or 5 different.
function range(n, m, callback, k){
if(!k) k=1;
while(true){
if(n<=m){
callback(n);
n+=k;
if(n>=m) break;
}else{
callback(n);
n-=k
if(n<=m) break;
}
}
}
you can execute the snippet by
range(10, 100,function(n){
// do here
},10)
you can extends the snippets by
function range_arr(n, m,callback,k) {
let a = []
range(n,m,function(n){
a.push(callback(n))
},k)
return a;
}
you can use this snippet by
let arr=range_arr(0,10,function(n){
return n=n*n;
},2);
(5)[0, 4, 16, 36, 64]
I realize this is an extremely old question and you probably don't need it anymore, but just in case someone reads this, my favorite way to do this is using a generator:
function* rangeGenerator(start, end = null, step = 1) {
if (end == null) {
end = start
start = 0
}
if (Math.sign(end - start) !== Math.sign(step)) {
step *= -1
}
while (Math.sign(step) === 1 ? start < end : start > end) {
yield start
start += step
}
}
const range = (start, end = null, step = 1) => [...rangeGenerator(start, end, step)]
This way, the range function will generate an array that goes from start (inclusive) to end (exclsive). step should always be provided as a positive number because the sign is automatically handled by the generator according to whiche of start and end is bigger. You can pass it a negative step, but it's unnecessary.
This gives you the ability to use a for of loop over the range
for (let i = 0; i < 100; i++) {}
// Is the same as
for (const i of range(100)) {}
Related
function newtonRaphson(x, count){
while(count > 0){
var previousValue = 0
if(previousValue === 0){
previousValue = x/2
}
var y = (previousValue + (x/previousValue))/2
previousValue = y
console.log(previousValue, count)
return newtonRaphson(x, count - 1)
}
return y
}
I have this function that is supposed to perform the Newton Raphson method, it would look like this:
x = 20
count = 4
previousValue(0) = 10
y(1) = (10 + (20/10))/2 = 6
previousValue = 6
y(2) = (6 + (20/6))/2 = 4.666
previousValue = 4.666
y(3) = (4.666 + (20/4.666))/2 = 4.476
previousValue = 4.476
y(4) = (4.476+ (20/4.476))/2 = 4.472
My question is, is there a way I can maintain previousValue between iterations without passing it to the function? Because x needs to be passed no matter what, as well as count, but I also need to tell the function what the previousValue is, so I'm at a loss
This code is confused.
First off, while it's called newtonRaphson, it seems to be only for a very specific case of the Newton-Raphson method, that of finding square roots using the Babylonian Method. So ideally it should have a better name.
It tries to simultaneously solve the problem recursively and with a while-loop. We need to choose one or the other. Here we choose a recursive version.
Then we note that these lines make no real sense:
var previousValue = 0
if(previousValue === 0){
previousValue = x/2
}
This is a ridiculously round-about way of writing
var previousValue = x/2
You ask about doing this without passing the previous value to the recursive function. You could, just by tracking it at a higher scope. (Please, please, not the global scope!) It might look like this:
const sqrt = (x, count) => {
let prev = x / 2
const _sqrt = (count) => {
if (count <= 0) return prev
prev = (prev + (x / prev)) / 2
return _sqrt (count - 1)
}
return _sqrt (count)
}
console .log (sqrt (25, 1)) //=> 7.25
console .log (sqrt (25, 2)) //=> 5.349137931034482
console .log (sqrt (25, 3)) //=> 5.011394106532552
console .log (sqrt (25, 4)) //=> 5.000012953048684
console .log (sqrt (25, 5)) //=> 5.000000000016778
console .log (sqrt (25, 6)) //=> 5
But I would not recommend this at all. Recursion is simplest when you pass the necessary variables into the function. And this is not hard to do, either with a defaulted parameter, like this:
const sqrt = (x, count, prev = x / 2) =>
count <= 0
? prev
: sqrt (x, count - 1, (prev + (x / prev)) / 2)
or with a public wrapper around an internal recursive helper function, like this:
const _sqrt = (x, count, prev) =>
count <= 0
? prev
: _sqrt (x, count - 1, (prev + (x / prev)) / 2)
const sqrt = (x, count) =>
_sqrt (x, count, x / 2)
Of the two, I usually prefer the defaulted parameter, but there are some potential problems with them, especially if you are not in control of how your function is called. Then the public/internal split makes sense.
I have been trying to wrap my head around this codility question for 1H30,and how to solve with binary search. I found the answer but I cant understand the logic behind it. Can someone who gets it kindly walk me through this answer.
This is the question
Task description
You are given integers K, M and a non-empty zero-indexed array A
consisting of N integers. Every element of the array is not greater
than M.
You should divide this array into K blocks of consecutive elements.
The size of the block is any integer between 0 and N. Every element of
the array should belong to some block.
The sum of the block from X to Y equals A[X] + A[X + 1] + ... + A[Y].
The sum of empty block equals 0.
The large sum is the maximal sum of any block.
For example, you are given integers K = 3, M = 5 and array A such
that:
A[0] = 2 A[1] = 1 A[2] = 5 A[3] = 1 A[4] = 2 A[5] = 2
A[6] = 2
The array can be divided, for example, into the following blocks:
[2, 1, 5, 1, 2, 2, 2], [], [] with a large sum of 15; [2], [1, 5, 1,
2], [2, 2] with a large sum of 9; [2, 1, 5], [], [1, 2, 2, 2] with a
large sum of 8; [2, 1], [5, 1], [2, 2, 2] with a large sum of 6.
The goal is to minimize the large sum. In the above example, 6 is the
minimal large sum.
Write a function:
function solution(K, M, A);
that, given integers K, M and a non-empty zero-indexed array A
consisting of N integers, returns the minimal large sum.
For example, given K = 3, M = 5 and array A such that:
A[0] = 2 A[1] = 1 A[2] = 5 A[3] = 1 A[4] = 2 A[5] = 2
A[6] = 2
the function should return 6, as explained above.
Assume that:
N and K are integers within the range [1..100,000];
M is an integer within the range [0..10,000];
each element of array A is an integer within the range [0..M].
This is the answer I could get my hands on
function solution(K, M, A) {
var begin = A.reduce((a, v) => (a + v), 0)
begin = parseInt((begin+K-1)/K, 10);
var maxA = Math.max(A);
if (maxA > begin) begin = maxA;
var end = begin + M + 1;
var res = 0;
while(begin <= end) {
var mid = (begin + end) / 2;
var sum = 0;
var block = 1;
for (var ind in A) {
var a = A[ind];
sum += a;
if (sum > mid) {
++block;
if (block > K) break;
sum = a;
}
}
if (block > K) {
begin = mid + 1;
} else {
res = mid;
end = mid - 1;
}
}
return res;
}
I would like to give the more detailed explanation of the algorithm that I have implemented and then one correct implementation in C++.
Find the maximum element in the input array. We could also use M, but M does not necessarily occur. A smaller number could be the maximum, so it is slight optimisation.
Calculate the sum of the input array. This would be the maximum largest sum.
Apply binary search, where the start is the maximum element and the end is the sum. The minimum largest sum would be in this range.
For each trial, check whether we can squeeze the elements into fewer blocks than the block number requested. If it is fewer, it is okay because we can use empty blocks. If it is equal, that is also acceptable. However, it is greater, then we can conclude that the tried minimum largest sum needs to be higher to allow individual blocks to be larger to reduce the block count.
One general principle can be observed above that the more fairly we distribute the sums of the blocks, the largest will become the minimum possible. For this, we need to squeeze as many elements into an individual block as possible.
If the number of blocks for a tried minimum largest sum is smaller than the expected block count, then we can try a slightly smaller minimum largest sum, otherwise we have to try a bit greater until we eventually find the best number.
As far as runtime complexity goes, the solution is O(n * log(N * M)) because the binary search is logarithmic. The sum can be N number of times the maximum element M at worst, which results in an N * M range to bisect with binary search. The inner iteration will go through all the elements, so that is N times. Therefore, it is O(N * log(N * M)) which is equivalent to O(N * log(N + M).
int check(vector<int>& A, int largest_sum)
{
int sum = 0;
int count = 0;
for (size_t i = 0; i < A.size(); ++i) {
const int e = A[i];
if ((sum + e) > largest_sum) { sum = 0; ++count; }
sum += e;
}
return count;
}
int solution(int K, int /*M*/, vector<int> &A)
{
int start = *max_element(A.begin(), A.end());
int end = accumulate(A.begin(), A.end(), 0);
while (start <= end) {
int mid = (start + end) / 2;
if (check(A, mid) < K) end = mid - 1;
else start = mid + 1;
}
return start;
}
This is a binary search on the solution. For each candidate solution, we iterate over the whole array once, filling array blocks to the maximum sum the block can be before exceeding the candidate. If the sum is not achievable, there is no point in trying a smaller sum so we search the space of higher possible candidates. And if the sum is achievable, we try the space of lower candidates, while we can.
I have changed a little bit the code so it's more clear, but here is my explanation:
/*
K = numberOfBlocks
M = maxNumber
A = array
*/
function solution(numberOfBlocks, maxNumber, array) {
let begin = array.reduce((a, b) => (a + b), 0); // Calculate total sum of A
begin = Math.ceil(begin / numberOfBlocks); // Calculate the mean of each theorethical block
begin = Math.max(begin, Math.max(...array)); // Set begin to the highest number in array if > than the mean
// In short: begin is now the smallest possible block sum
// Calculate largest possible block sum
let end = begin + maxNumber + 1;
var result = 0;
while (begin <= end) {
// Calculate the midpoint, which is our result guess
const midpoint = (begin + end) / 2;
let currentBlockSum = 0;
let block = 1;
for (let number of array) {
currentBlockSum += number;
// If currentBlockSum > midpoint means that we are
// in a different block...
if (currentBlockSum > midpoint) {
++block;
// ...so we reset sum with the current number
currentBlockSum = number;
// but if we are out of blocks, our guess (midpoint) is incorrect
// and we will have to adjust it
if (block > numberOfBlocks)
break;
}
}
// If we are out of blocks
// it means that our guess (midpoint) is bigger than we thought
if (block > numberOfBlocks) {
begin = midpoint + 1;
// else, it's smaller
} else {
result = midpoint;
end = midpoint - 1;
}
}
return result;
}
Looking and testing at the solutions, none of them actually work.
I decided to spend some time on it, here is my solution (working for any use case with maximum performance).
using System;
using System.Linq;
class Solution
{
public int solution(int K, int M, int[] A)
{
int start = Math.Max((int)Math.Ceiling((decimal)A.Sum()/(decimal)K), A.Max());
int end = A.Sum();
int result = 0;
while(start <= end)
{
int dicotomie = (end + start) / 2;
if(calculateNbBlocks(dicotomie, A) <= K)
{
result = dicotomie;
end = dicotomie - 1;
}
else
start = dicotomie + 1;
}
return result;
}
public int calculateNbBlocks(int dicotomie, int[] A)
{
int nbBlocks = 1;
int sum = 0;
for(int i=0; i<A.Length; i++)
{
sum += A[i];
if(sum > dicotomie)
{
sum = A[i];
nbBlocks++;
}
}
return nbBlocks;
}
}
Scored 100% on Codility (https://app.codility.com/demo/results/trainingQYJ68K-KJR/) using const midpoint = Math.floor((begin + end) / 2);
instead of
const midpoint = (begin + end) / 2;
after copying Javascript code in answer by pytness (Nov 5, 2020 at 8:40).
/*
K = numberOfBlocks
M = maxNumber
A = array
*/
function solution(numberOfBlocks, maxNumber, array) {
let begin = array.reduce((a, b) => (a + b), 0); // Calculate total sum of A
// console.log("total sum of A: ", begin);
begin = Math.ceil(begin / numberOfBlocks); // Calculate the mean of each theoretical block
// console.log('Math.ceil(begin / numberOfBlocks): ', begin);
begin = Math.max(begin, Math.max(...array)); // Set begin to the highest number in array if > than the mean
// console.log('Math.max(begin, Math.max(...array)): ', begin);
// In short: begin is now the smallest possible block sum
// Calculate largest possible block sum
let end = begin + maxNumber + 1;
// console.log("end: ", end);
var result = 0;
while (begin <= end) {
// Calculate the midpoint, which is our result guess
const midpoint = Math.floor((begin + end) / 2);
// console.log("midpoint: ", midpoint);
let currentBlockSum = 0;
let block = 1;
for (let number of array) {
currentBlockSum += number;
// console.log("currentBlockSum: ", currentBlockSum);
// If currentBlockSum > midpoint means that we are
// in a different block...
if (currentBlockSum > midpoint) {
// console.log("currentBlockSum > midpoint");
++block;
// console.log("block: ", block);
// ...so we reset sum with the current number
currentBlockSum = number;
// console.log("currentBlockSum: ", currentBlockSum);
// but if we are out of blocks, our guess (midpoint) is incorrect
// and we will have to adjust it
if (block > numberOfBlocks) {
// console.log("block > numberOfBlocks before break");
// console.log("block: ", block);
// console.log("break");
break;
}
}
}
// If we are out of blocks, it means that our guess for midpoint is too small.
if (block > numberOfBlocks) {
// console.log("block > numberOfBlocks before begin");
begin = midpoint + 1;
// console.log("begin: ", begin);
}
// Else, it's too big.
else {
// console.log("block <= numberOfBlocks");
result = midpoint;
// console.log("result: ", result);
end = midpoint - 1;
// console.log("end: ", end);
}
}
// console.log("result: ", result);
return result;
}
WARNING: Probably not a typeError. Doing scripting algorithms for fun. Trying to learn something/sharpen problem solving => I commented out the undefined array. I can't tell why it's throwing an error.
function arrayManipulation(n, queries) {
var array = new Array(n).fill(0)
var x = 0
var recurring = (argmnt, query, y) => {
//problem start here
var start = query[0],
end = query[1]
//problem end somewhere else
var newArg = argmnt.map((el, index) =>{
if(index+1 >= start && index+1 <= end){
return query[2] + el
}else{
return el
}
})
console.log(newArg)
if ( y < queries.length ){
y += 1
recurring(newArg, queries[y], y)
}else{
return newArg
}
}
var solution = recurring(array, queries[x], x)
}
arrayManipulation(5, [[1, 2, 100], [2, 5, 100], [3, 4, 100]])
Although the script runs fine and give me the desired output. It throws a stderr in node or typeerror in JS. I just want to know why that is.
The problem is with your y variable. In your check y < queries.length you first check for the length and then increment.
If your array has a length of 3 (as it does in your example), you will check if y is less than 3 and if it’s 2, it passes the test, gets incremented to 3 and then you pass queries[3] to the next recurring() invocation. The last element of queries is queries[2], though. So queries[3] is undefined and inside the function, you try query[0] which is accessing property 0 of undefined. That’s the error.
function arrayManipulation(n, queries) {
var array = new Array(n).fill(0)
var x = 0
var recurring = (argmnt, query, y) => {
//problem start here
var start = query[0],
end = query[1]
//problem end somewhere else
var newArg = argmnt.map((el, index) =>{
if(index+1 >= start && index+1 <= end){
return query[2] + el
}else{
return el
}
})
console.log(newArg)
y += 1 // now is here
if ( y < queries.length ){
// was here
recurring(newArg, queries[y], y)
}else{
return newArg
}
}
var solution = recurring(array, queries[x], x)
}
arrayManipulation(5, [[1, 2, 100],
[2, 5, 100],
[3, 4, 100]])
You have two choices: either increment first and then do the check (as in the snippet above), or check for queries.length - 1, like this:
if ( y < queries.length - 1 ){
Also, a bit of an optimization. You don’t need the y variable. It’s basically just x but passed along as an argument. You have access to x from recurring() so you might as well use it:
function arrayManipulation(n, queries) {
var array = new Array(n).fill(0)
var x = 0
var recurring = (argmnt, query) => {
//problem start here
var start = query[0],
end = query[1]
//problem end somewhere else
var newArg = argmnt.map((el, index) => {
if (index + 1 >= start && index + 1 <= end) {
return query[2] + el
} else {
return el
}
})
console.log(newArg)
x += 1
if (x < queries.length) {
recurring(newArg, queries[x], x)
} else {
return newArg
}
}
var solution = recurring(array, queries[x])
}
arrayManipulation(5, [
[1, 2, 100],
[2, 5, 100],
[3, 4, 100]
])
Here is what I got from your script:
{
"message": "Uncaught TypeError: Cannot read property '0' of undefined",
"filename": "https://stacksnippets.net/js",
"lineno": 20,
"colno": 22
}
This is normal because of this part:
if ( y < queries.length ){
y += 1
recurring(newArg, queries[y], y)
You have 3 queries. At one point, y will equal 2.
It will be incremented and will equal 3.
queries[3] doesn't exist and will be undefined. (because, you do know that an array's first index is 0, do you?)
Then, in the next recurring, you'll call query[0] on it, but it is undefined: it will crash.
That's all ;) You if condition is not good ;)
It must be if ( y < queries.length - 1 )
Is there any ECMAScript 6/7 equivalent to underscore’s range function?
In underscore:
_.range(startPage, endPage + 1);
In ES2015:
Array.from(Array(n), (_, i) => x + i)
Not sure how the ES2015 version works. I would like to know how range in ecmascript of javascript works
The idea is to create an array of length end - start + 1, and then fill it with the relevant numbers using Array#from.
The Array.from() method creates a new Array instance from an
array-like or iterable object.
In this case Array#from needs an object with the length property. Using Array(n) creates such an object (array). You can also use { length: n } directly. In this case n = Math.abs(end - start) + 1.
Array#from accepts a mapFn callback, a function that can transform the iterated value. The function receives 2 params - the values (which we can ignore in this case), and the index (0 based). Adding start to the current index will create the numbers in the range.
const range = (start, end) => Array.from(
Array(Math.abs(end - start) + 1),
(_, i) => start + i
);
console.log(range(5, 10));
console.log(range(-10, -5));
console.log(range(-5, 10));
This version will handle reverse range as well (large to small) as well:
const range = (start, end) => {
const inc = (end - start) / Math.abs(end - start);
return Array.from(
Array(Math.abs(end - start) + 1),
(_, i) => start + i * inc
);
};
console.log(range(5, 10));
console.log(range(-5, -10));
console.log(range(10, -5));
Note that the following implementation does not allow for lazy-generated lists:
Array.from(Array(n), (_, i) => x + i)
Imagine that you need a list of 1M numbers:
range(1, 1000000);
Are you going to consume all of them? Maybe not, yet all the numbers have been generated already and they probably left a non-trivial footprint on your memory.
It would be nice if we could get them one by one on demand.
It turns out we can do exactly that with a generator:
function* range(start, end, step = 1) {
for (let value = start; value < end; value += step) {
yield value;
}
}
for (let x of range(1, Infinity)) {
if (x > 10) {
break;
}
console.log(x);
}
Did you notice the range(1, Infinity) bit?
In a classic implementation where all the numbers are generated in advance, this code wouldn't even run as you would get stuck in an infinite number-generating loop.
As I understand it, the range method generates an array of numbers starting from start, stepping with step (default = 1) until end. This simple function does just that :-)
const range = (start, end, step = 1) => {
let result = [];
for(let i=0; i<=end; i+=step) {
result.push(i);
}
return result;
};
What is nice here is that you can use decimal step values.
For example:
range(0, 2, 0.5) // [ 0, 0.5, 1, 1.5, 2 ]
range(0,100,20) // [ 0, 20, 40, 60, 80, 100 ]
Trying to compute a ratio in a fast efficient method when confronted with a large set of numbers. The idea is an array with numbers example Box=[1, 2, 3, 4] and naming name each item in the array to 1=A, 2=B, 3=C, 4=D. then using a number call step. Am trying to achieve this for every element in the box, for example in case of the first element A with 3 steps only.
At step 1 take the value of A and divide it by box sum
step 1 => 1/10 = 0.1
At step 2 take combination BA, CA, DA and divide by box sum subtracting each time by previous value
=> (2/10)(1/8) + (3/10)(1/7) + (4/10)(1/6) = 0.1345
At step 3 take combination BCA, BDA, CBA, CDA, DBA, DCA and divide by box sum subtracting each time by previous value
=> (2/10)(3/8)(1/5) + (2/10)(4/8)(1/4) + (3/10)(2/7)(1/5) + (3/10)(4/7)(1/3) + (4/10)(2/6)(1/4) + (4/10)(3/6)(1/3) = 0.2143
and finally return the sum of all the steps for A=0.1 + 0.1345 + 0.2143 = 0.4488
if the same procedural is done to all elements in the box array B= 0.7587 , C= 0.8702, D= 0.9222 to test if calculation was correct the sum of the ratios is equal to 3 the same number of steps used.
If steps and elements in the box are increased it should do all combination in the same order. Can this calculation run fast and not run out of memory if the Box size is 1000 and with 10 steps for every element in the box.
recurrence formula for an array length 4 with 3 steps
[A,B,C,D]
for element A
step 1 : A
step 2 : BA CA DA
step 3 : BCA BDA CBA CDA DBA DCA
for element B
step 1 : B
step 2 : AB CB DB
step 3 : ACB ADB CAB CDB DAB DCB
for element C
step 1 : C
step 2 : AC BC DC
step 3 : ABC ADC BAC BDC CAD CBD
for element D
step 1 : D
step 2 : AD BD CD
step 3 : ABD ACD BAD BCD DAC DBC
for an array of 5 elements and 4 steps [A, B, C, D, E]
for element A :
step 1: A
step 2: BA CA DA EA
step 3: BCA BDA BEA CBA CDA CEA DBA DCA DEA EBA ECA EDA
step 4:
BCDA
BCEA
BDCA
BDEA
BECA
BEDA
CBDA
CBEA
CDBA
CDEA
CEBA
CEDA
DBCA
DBEA
DCBA
DCEA
DEBA
DECA
EBCA
EBDA
ECBA
ECDA
EDBA
EDCA
Here is my try in Matlab code if JS is not possible is there a way to improve the Matlab code instead
% sample code to test
userTickets = [1, 2, 3, 4];
users = [0, 0, 0, 0];
userCount = 4;
draws = 3;
for player = 1 : length(users)
userLuck = 0;
for draw = 1 : prizes
userLuck = userLuck + cardCombo(player, userTickets, draw);
end
users(player) = userLuck;
end
total = sum(users);
**** npermutek
function [A, I] = npermutek(v,k)
narginchk(2,3);
nck = nchoosek(1:numel(v),k);
permutes = perms(1:k);
I = nck(:,permutes);
I = reshape(I,[],k);
I = sortrows(I,1:k);
A = v(I);
end
**** cardCombo
function [result] = cardCombo(player, cards, draw)
playerCards = cards(player);
opponentCards = cards(setdiff(1:end,player));
cardCount = sum(cards);
if draw == 1
result = playerCards/cardCount;
elseif draw == 2
result = 0;
for n = 1 : length(opponentCards)
result = result + ((opponentCards(n)/cardCount) * (playerCards/ (cardCount - opponentCards(n))));
end
else
combo = npermutek(opponentCards, draw -1);
combWithPlayer = repmat(playerCards, size(combo,1),1);
combo = [combo combWithPlayer];
rowSize = size(combo, 1);
colSize = size(combo,2);
rowSum = 0;
for row = 1 : rowSize
rowMul = 1;
sub = 0;
for col = 1 : colSize
num = combo(row, col);
rowMul = rowMul * (num / (cardCount - sub));
sub = sub + num;
end
rowSum = rowSum + rowMul;
end
result = rowSum;
end
end
Original method
proba of player P winning at rank exactly k is:
p(k) = sum_{a={A(k-1, N-1)}}( p(a) * p(P|a) )
where
N is the number of players
{A(k-1, N-1)} is the set of all arrangements of k-1 elem among N-1
p(a) the proba of an arrangement from {A(k-1, N-1)}
proba g of winning in [1:k]
g(k) = sum_{i=1}^k p(i)
Alternative method
An other way: to win either step 1 or ... or step 10 is the opposite of losing in every step
that is...take all arrangements of size k such that no P appear: A(k, N-1)
g(k) = 1 - sum_{a={A(k, N-1)}} p(a)
while shorter formula, the arrangements have a bigger size (k instead of k-1) so it will take more time... than original method
At this point, optimization could be to focus on evaluating the arrangements
we might try an exponentiation:
compute all {A(2, N-1)}
then {A(4, N-1)} by reusing {A(2, N-1)} (join them when no two letters in common)
then concatenate {A(4, N-1)} x {A(4, N-1)} x {A(2, N-1)} still with the condition of no two letters in common
But: notice the card of |{A(4, N-1)}| is 999*998*997*996 ~= 1e12 (and I don't dare taking such cartesian product)
Approximation
Maybe there is better way, but as a fallback, one can simply use a monte carlo approach:
Make N simu
Draw k random numbers. If any number from the draw lands in the playerInterval, success
return nbSuccess/N
To avoid resizing an array, consider a set([1,2,3,...]) and construct virtually the cumulative interval
]0;1] U ]1;1+2] U ]3;3+3] ... etc
draw initially from den == (n*(n+1)/2), and check the landing i-th interval
If i !== player, remove i from the set and reconsider the new cumulative interval, redraw from den-i
and so forth until k (or success)
function A (k, v, cbk) {
function rec(hand, depth){
if (depth == k) return cbk(hand)
for (let i = 0; i < v.length; ++i) {
let x = v[i]
hand[depth] = x
v.splice(i, 1)
rec(hand, depth + 1)
v.splice(i, 0, x)
}
}
return rec(Array(k), 0)
}
function p (v, den) {
return v.reduce((acc, num) => {
acc.res *= num / acc.den
acc.den -= num
return acc
}, { den, res: 1 })
}
//original method
function orig (box, player, steps) {
const boxWithoutPlayer = box.slice(0)
boxWithoutPlayer.splice(box.indexOf(player), 1)
const total = box.reduce((s, x) => s + x, 0)
let s = player / total
for (let step = 1; step < steps; ++step) {
A(step, boxWithoutPlayer, arr => {
let { den, res } = p(arr, total)
s += res * p([player], den).res
})
}
return s
}
//alternative method
function neg (box, player, steps) {
const boxWithoutPlayer = box.slice(0)
boxWithoutPlayer.splice(box.indexOf(player), 1)
const total = box.reduce((s, x) => s + x, 0)
let s = 0
A(steps, boxWithoutPlayer, arr => {
s += p(arr, total).res
})
return 1 - s
}
//montecarlo approx
function mc (box, player, steps) {
const NSimu = 1e5
let success = 0
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
const getRandomInt = max => Math.floor(Math.random() * Math.floor(max))
const total = box.reduce((s, x) => s + x, 0)
for (let i = 0; i < NSimu; ++i) {
let set = new Set(box)
let den = total
for (let s = 0; s < steps; ++s) {
// n in [1; den]
const n = getRandomInt(den) + 1
let idx = 0
let it = 0
for(const x of set){
if (n <= it + x) {
idx = x
break
}
it += x
}
if (idx == player) {
success++
break
}
den -= idx
set.delete(idx)
}
}
return success / NSimu
}
function main(box, player, steps, meths) {
const f = function(key, meth){
return _=> {
console.time(key)
console.log(`player ${player} winning: `,meth(box, player, steps))
console.timeEnd(key)
}
}
const dic = {
orig: f('orig', orig),
neg: f('neg', neg),
mc: f('mc', mc),
}
meths.forEach(m => dic[m]())
console.log('----')
}
main([...Array(6)].map((x,i) => i + 1), 3, 3, ['orig', 'neg', 'mc'])
main([...Array(10)].map((x,i) => i + 1), 8, 7, ['orig', 'neg', 'mc'])
//takes 1000; 900; 10 about 8s on node on my machine
main([...Array(500)].map((x,i) => i + 1), 250, 10, ['mc'])