Maximum call stack size exceeded error during recursive calls - javascript

I've been trying to solve a recursion question islandPerimeter
/**
* #param {number[][]} grid
* #return {number}
*/
var islandPerimeter = function (grid) {
let visitedLand = new Set();
function dfs(i, j) {
if (i < 0 || j < 0 || i > grid.length || j > grid[0].length || grid[i][j] == 0) return 0;
if (visitedLand.has({ i: j })) return 1;
visitedLand.add({ i: j });
let perimeter = dfs(i, j + 1);
perimeter += dfs(i + 1, j);
perimeter += dfs(i, j - 1);
perimeter += dfs(i - 1, j);
return perimeter;
}
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
if (grid[i][j]) {
return dfs(i, j);
}
}
}
};
let nums = [
[0, 1, 0, 0],
[1, 1, 1, 0],
[0, 1, 0, 0],
[1, 1, 0, 0],
];
islandPerimeter(nums);
I've initialised this function of dfs()inside my main function but it shows this...
function dfs(i, j) {
^
RangeError: Maximum call stack size exceeded
if (i < 0 || j < 0 || i > grid.length || j > grid[0].length || grid[i][j] == 0)
^
TypeError: Cannot read properties of undefined (reading '1')
There's two for() loops that first calls this dfs function and inside it I'm again calling them.
Please explain what's this issue, I've read a few threads that this is regarding over calling the recursion. Anyway to fix this?

When the expression {i: j} occurs twice in your code, they are two distinct objects. Therefore, the Set does not recognize the second object as the same as the first:
> visitedLand.has({1: 2})
false
> visitedLand.add({1: 2})
Set(1) { { '1': 2 } }
> visitedLand.has({1: 2})
false
As a consequence, the return 1; statement is never reached, and the recursion goes on ad infinitum.
Use strings i + "-" + j rather than objects.
Also, replace
i > grid.length || j > grid[0].length
with
i >= grid.length || j >= grid[0].length
because grid[grid.length] is already undefined.

Related

Storing index - value pairs in a map then check with an array

I have two arrays: data and usedIndexes. First, I need to find all dividable (to another number in the array) numbers and store them with their indexes (let's say in myMap) and then somehow go through the second array "usedIndexes" and find dividable pairs that one of their index number is in usedIndexes array.
For example, given arrays:
data: [8, 3, 5, 2, 7, 9, 50]
usedIndexes = [0, 1, 6]
const myMap = {}
for (let i = 0; i < arr.length; i++) {
for(let y = i + 1; y < arr.length; y++) {
if(arr[i] % arr[y] === 0 || arr[y] % arr[i] === 0 ) {
if(!myMap[i]) {
myMap[i] = arr[i]
}
if(!myMap[y]) {
myMap[y] = arr[y];
}
}
}
}
So this will give me something like (index - value pair), myMap:
{
0: 8,
3: 2,
1: 3,
5: 9,
2: 5,
6: 50
3: 2,
6: 50
}
So actual dividable pairs like:
8 - 2
3 - 9
5 - 50
2 - 50
Quite frankly, I am not sure maybe map/object is not the best data structure to hold these values but I couldn't think something else.
Now, how can I loop through the usedIndexes array and find key-value pairs which at least one of the keys has in usedIndexes. Please read the comment in code sample below.
for (let i = 0; i < usedIndexes.length; i++){
// e.g. usedIndexes first element is 0 and myMap has 0:8
// but what I need to return 0:8 AND 3:2 because dividable pair is 8 - 2.
}
Don't store the divisor value in the map, store its index:
const myMap = {};
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; j++) {
if (i != j && arr[i] % arr[j] === 0) {
myMap[i] = j;
myMap[j] = i;
}
}
}
Then it's only a matter of going through usedIndexes to find those that appear as part of a pair in the map:
const result = [];
for (const index of usedIndexes) {
if (index in myMap) {
const other = myMap[index];
result.push([{index, value: arr[index]}, {index: other, value: arr[other]}]);
}
}
console.log(result);
However, this will not find all possible pair where one value divides the other and one value is referenced by a usedIndex. This is due to myMap storing only a single pair for each index, even when an index would be included in multiple pairs. (For simplicity I didn't bother to check whether the assignment would override an other value in the first snippet, it only changes whether the first or last combination would be found, it doesn't solve the problem).
To alleviate this problem, we should just get rid of the myMap and fill the result right inside the nested loop:
const result = [];
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; j++) {
if (i != j && arr[i] % arr[j] === 0) {
if (usedIndexes.includes(i) || usedIndexes.includes(j)) {
result.push([{index: i, value: arr[i]}, {index: j, value: arr[j]}]);
}
}
}
}
console.log(result);
The only problem with this solution is its inefficiency - usedIndexes.includes does iterate the usedIndexes array every time. You could alleviate that by creating a Set of indices then use .has() inside the loop, but we can actually do better: just don't iterate those indices in the first place!
const result = [];
for (const i of usedIndexes) {
for (let j = 0; j < arr.length; j++) {
if (i != j) {
const [a, b] = arr[i] > arr[j] ? [i, j] : [j, i];
if (arr[a] % arr[b] === 0) {
result.push([{index: a, value: arr[a]}, {index: b, value: arr[b]}]);
}
}
}
}
console.log(result);

JS: Given an array, find element pairs whose sum equal the target value

Yes, as the title suggests
Given an array arr, find element pairs whose sum equal the second argument arg and return the sum of their indices.
What I have done so far
function pairwise(arr, arg) {
if (arr.length === 0) return 0
let res = [];
let indexes = [];
let indexArr = []
for (let i = 0; i < arr.length - 1; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arg === arr[i] + arr[j] && !indexes.includes(i) && !indexes.includes(j)) {
res.push([arr[i], arr[j]]);
indexes.push(i);
indexes.push(j);
}
}
}
console.log(res)
for (let i = 0; i < res.length; i++) {
for (let j = 0; j < res[i].length; j++) {
indexArr.push(arr.indexOf(res[i][j]))
}
}
return indexArr.reduce((curr, prev) => curr + prev)
}
pairwise([1, 1, 1], 2);
I am doing this in Freecodecamp. It passes some test but fails the following tests:
pairwise([1, 1, 1], 2) should return 1. (above code returns 0)
pairwise([0, 0, 0, 0, 1, 1], 1) should return 10. (above code returns 8)
I think I am doing wrong in the indexOf part. How to solve this?

HeapSort implementation in Javascript

I am learning about heaps and I wanted to implement the heap sort algorithm in Javascript using MinHeap.
The issue is that I keep getting a non-sorted array.
I even tried to just translate a working algorithm from C++ to Javascript.
Orginal algorithm link: https://www.geeksforgeeks.org/heap-sort-for-decreasing-order-using-min-heap/
C++:
// To heapify a subtree rooted with node i which is
// an index in arr[]. n is size of heap
void heapify(int arr[], int n, int i)
{
int smallest = i; // Initialize smalles as root
int l = 2 * i + 1; // left = 2*i + 1
int r = 2 * i + 2; // right = 2*i + 2
// If left child is smaller than root
if (l < n && arr[l] < arr[smallest])
smallest = l;
// If right child is smaller than smallest so far
if (r < n && arr[r] < arr[smallest])
smallest = r;
// If smallest is not root
if (smallest != i) {
swap(arr[i], arr[smallest]);
// Recursively heapify the affected sub-tree
heapify(arr, n, smallest);
}
}
// main function to do heap sort
void heapSort(int arr[], int n)
{
// Build heap (rearrange array)
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
// One by one extract an element from heap
for (int i = n - 1; i >= 0; i--) {
// Move current root to end
swap(arr[0], arr[i]);
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
Javascipt (translated code):
function swap(arr, i, j){
const c = arr[i];
arr[i] = arr[j];
arr[j] = c;
}
function heapify(arr, n, i)
{
let smallest = i; // Initialize smalles as root
let l = 2 * i + 1; // left = 2*i + 1
let r = 2 * i + 2; // right = 2*i + 2
// If left child is smaller than root
if (l < n && arr[l] < arr[smallest])
smallest = l;
// If right child is smaller than smallest so far
if (r < n && arr[r] < arr[smallest])
smallest = r;
// If smallest is not root
if (smallest != i) {
swap(arr[i], arr[smallest]);
// Recursively heapify the affected sub-tree
heapify(arr, n, smallest);
}
}
// main function to do heap sort
function heapSort(arr, n)
{
// Build heap (rearrange array)
for (let i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
// One by one extract an element from heap
for (let i = n - 1; i >= 0; i--) {
// Move current root to end
swap(arr[0], arr[i]);
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
when I try with this array arr = [1,2,7,3,5], the heapSort algorithm returns this table [ 1, 2, 7, 3, 5 ];
Can you please help me figure out what's wrong with the JS implementation?
thank you in advance!
This code should go fine:
const heapify = (arr, length, i) => {
let largest = i
const left = i * 2 + 1
const right = left + 1
if (left < length && arr[left] > arr[largest]) {
largest = left
}
if (right < length && arr[right] > arr[largest]) {
largest = right
}
if (largest !== i) {
[arr[i], arr[largest]] = [arr[largest], arr[i]]
heapify(arr, length, largest)
}
return arr
}
const heapSort = arr => {
const length = arr.length
let i = Math.floor(length / 2 - 1)
let k = length - 1
while (i >= 0) {
heapify(arr, length, i)
i--
}
while (k >= 0) {
[arr[0], arr[k]] = [arr[k], arr[0]]
heapify(arr, k, 0)
k--
}
return arr
}
const arr = [4, 6, 3, 2, 9];
sortedArr = heapSort(arr);
console.log("Sorted array is \n", sortedArr)
I took it from here. Take a look at the post if you are more interested in how it's implemented. It's very well explained.
UPDATE
Ok so, about your code, I see exactly 2 problems:
You are incorrectly using the "swap" function. Just change swap(arr[i], arr[smallest] by swap(arr, i, smallest); and swap(arr[0], arr[i]) by swap(arr, 0, i). Also, if you want to use the latest ES6 features you can swap elements in an array without implementing that "swap" function, just like this: [arr[0], arr[2]] = [arr[2], arr[0]] (this will swap the element at position 0 with the element at position 2). This is called destructuring assignment.
In the first for loop in your "heapSort" function, initialize i variable to an integer (notice that n / 2 could be a float). You can do it like this: let i = Math.floor(n / 2 - 1).
Here I leave you the fixed code. I've executed it by myself and it works:
function swap(arr, i, j){
const c = arr[i];
arr[i] = arr[j];
arr[j] = c;
}
function heapify(arr, n, i)
{
let smallest = i; // Initialize smallest as root
let l = 2 * i + 1; // left = 2*i + 1
let r = 2 * i + 2; // right = 2*i + 2
// If left child is smaller than root
if (l < n && arr[l] < arr[smallest])
smallest = l;
// If right child is smaller than smallest so far
if (r < n && arr[r] < arr[smallest])
smallest = r;
// If smallest is not root
if (smallest != i) {
swap(arr, i, smallest);
// Recursively heapify the affected sub-tree
heapify(arr, n, smallest);
}
}
// main function to do heap sort
function heapSort(arr, n)
{
// Build heap (rearrange array)
for (let i = Math.floor(n / 2 - 1); i >= 0; i--)
heapify(arr, n, i);
// One by one extract an element from heap
for (let i = n - 1; i >= 0; i--) {
// Move current root to end
swap(arr, 0, i);
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
const arr = [4, 6, 3, 2, 9];
heapSort(arr, arr.length);
console.log("Sorted array is \n", arr)
Here is my version of heapsort.
This is non-recursive solution and modifies the original array.
function swap(arr, i, j) {
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
function heapify(arr, start = 0) {
for(let i = start;i < arr.length; i++) {
let j = i;
let root = start + Math.floor((j-start)/2);
while(( root >= start ) && (arr[root] < arr[j])) {
swap(arr, root, j);
j = root;
root = start + Math.floor((j-start)/2);
}
}
}
function heapSort(arr) {
for(let i = 0; i < arr.length;i++)
heapify(arr, i);
}
const arr = [1,2,8000,3,4,5,-1,200000,8000,-1,20000];
heapSort(arr);
console.log(arr);
const HeapSort = (arg) => {
const Income_arr = [...arg];
const Output_arr = [];
const InnerSort = () => {
const length = Income_arr.length;
for (let i = 0; i < Income_arr.length - 1; i++) {
let max = i;
const left = i + 1;
const right = i + 2;
// will change '>' or '<' depends on which order we want, like either descending or ascending order
if (i <= length && Income_arr[i] > Income_arr[left]) {
// swapping the array
[Income_arr[i], Income_arr[left]] = [Income_arr[left], Income_arr[i]];
}
if (i <= length && Income_arr[i] > Income_arr[right]) {
// swapping the array
[Income_arr[i], Income_arr[right]] = [Income_arr[right], Income_arr[i]];
}
}
Output_arr.push(Income_arr.shift()); // Add the largest Number in output_arr & remove the largest Number
return Income_arr;
};
for (let i = arg.length - 1; i >= 0; i--) {
// Run untill array length ends
InnerSort(); // To Find the largest number
}
console.log(Output_arr)
return Output_arr;
};
HeapSort([16, 20, 99, 34, 17, 15]);
HeapSort([16, 20, -99, 34, 17, 15]);
HeapSort([4, 20, 9, 34, 2, 15]);
const HeapSort = (arg) => {
const Income_arr = [...arg];
const Output_arr = [];
const InnerSort = () => {
const length = Income_arr.length;
for (let i = 0; i < Income_arr.length - 1; i++) {
let max = i;
const left = i + 1;
const right = i + 2;
// will change '>' or '<' depends on which order we want, like either descending or ascending order
if (i <= length && Income_arr[i] > Income_arr[left]) {
// swapping the array
[Income_arr[i], Income_arr[left]] = [Income_arr[left], Income_arr[i]];
}
if (i <= length && Income_arr[i] > Income_arr[right]) {
// swapping the array
[Income_arr[i], Income_arr[right]] = [Income_arr[right], Income_arr[i]];
}
}
Output_arr.push(Income_arr[length - 1]); // Add the largest Number in output_arr
Income_arr.pop(); // Remove the largest Number
return Income_arr;
};
for (let i = arg.length - 1; i >= 0; i--) {
// Run untill array length ends
InnerSort(); // To Find the largest number
}
console.log(Output_arr)
return Output_arr;
};
HeapSort([16, 20, 99, 34, 17, 15]);
HeapSort([16, 20, -99, 34, 17, 15]);
HeapSort([4, 20, 9, 34, 2, 15]);

Recursive algorithm fails to complete tests in allotted time

I was doing a test that required an algorithm for Binary Tomography. A set of 38 test values are supplied that test correctness, but there is also a time limit of 1 CPU sec to complete all the tests. The problem is as follows:
Output “Yes” if there exists an m-by-n matrix A, with each element either being 0 or 1, such that
Otherwise output “No”.
For each test, 2 arrays are provided:
r (the sum of each row in the matrix)
c (the sum of each column in the matrix)
In the equation:
m is the length of the r array, where 1 <= m
n is the length of the c array, where n <= 1000
ri is an element of r, where 0 <= ri <= n
cj is an element of c, where 0 <= cj <= m
A "Yes" example
m = 3;
n = 4;
r = [2, 3, 2];
c = [1, 1, 3, 2];
A "No" example
m = 3;
n = 3;
r = [0, 0, 3];
c = [0, 0, 3];
I have a solution that appears to give correct answers, however it only manages 12 / 38 tests before the 1 second of CPU time is exceeded.
I originally wrote the code in ES5 and then went back and converted to to ES3 to try and get more performance out of it. (originally managed 9 tests as ES5). There doesn't seem a great deal left that I can do to the current algorithm to improve the performance (unless I am mistaken). This leads me to believe that my algorithm is at fault an that there must be a faster algorithm for doing this. I did a ton of reading trying to find one and ended up with a headache :)
So I'm turning to the community to see if anyone can suggest a faster algorithm than I am currently using.
'use strict';
const ZEROS = (function (seed) {
let string = seed;
for (let i = 0; i < 19; i += 1) {
string += seed;
}
return string;
}('00000000000000000000000000000000000000000000000000'));
const ZEROSLEN = ZEROS.length;
const permutate = function (n, ri) {
const result = [];
const memoize = {};
let count = 0;
do {
const bin = count.toString(2);
if (ZEROSLEN + bin.length > ZEROSLEN + n) {
break;
}
if (!memoize[bin] && (bin.split('1').length - 1) === ri) {
const string = (ZEROS + bin).slice(-n);
const sLen = string.length;
const perm = new Array(sLen);
for (let i = sLen - 1; i >= 0; i -= 1) {
perm[i] = +string[i];
}
memoize[bin] = result.push(perm);
}
count += 1;
} while (count);
return result;
};
const getMatrixSum = function (n, matrix) {
const mLength = matrix.length;
const rows = new Array(mLength);
const a = new Array(n);
const last = mLength - 1;
for (let x = n - 1; x >= 0; x -= 1) {
for (let y = last; y >= 0; y -= 1) {
rows[y] = matrix[y][x];
}
let sum = 0;
for (let i = rows.length - 1; i >= 0; i -= 1) {
sum += rows[i];
}
a[x] = sum;
}
return a;
};
const isEqual = function (a, b) {
const length = a.length;
if (length !== b.length) {
return false;
}
for (let i = length - 1; i >= 0; i -= 1) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
};
const addRow = function (i, prev, r, c, result) {
if (result) {
return result;
}
const n = c.length;
const ri = r[i];
if (ri < 0 || ri > n) {
throw new RangeError('ri out of range');
}
const p = permutate(n, ri);
const m = r.length;
const rsLast = m - 1;
const nextI = i + 1;
for (let x = p.length - 1; x >= 0; x -= 1) {
const permutation = p[x];
const next = prev.slice();
next.push(permutation);
const sums = getMatrixSum(n, next);
if (i < rsLast) {
let memo = 0;
for (let j = sums.length - 1; j >= 0; j -= 1) {
if (sums[j] > c[j]) {
memo += 1;
}
}
if (!memo && addRow(nextI, next, r, c, result)) {
return true;
}
} else if (isEqual(sums, c)) {
return true;
}
}
return false;
};
const isSolvable = function (r, c) {
const m = r.length;
const n = c.length;
if (m < 1 || n > 1000) {
throw new Error('Bad data');
}
for (let j = n; j >= 0; j -= 1) {
const cj = c[j];
if (cj < 0 || cj > m) {
throw new RangeError('cj out of range');
}
}
return addRow(0, [], r, c, false) ? 'Yes' : 'No';
};
console.log(isSolvable([2, 3, 2], [1, 1, 3, 2]));
console.log(isSolvable([0, 0, 3], [0, 0, 3]));
It may be worth noting that the tests are being run on SpiderMonkey version JavaScript-C24.2.0
Refs:
https://en.wikipedia.org/wiki/Discrete_tomography
https://open.kattis.com/problems/tomography
Since permutations yield to brute force, they should be the last resort when developing algorithms similar to this one. Most of the time they are not needed.
As i have commented above, I have a feeling that one strategy could be first sorting the r and c arrays descending and start with the bigger ones. I haven't had time to implemented a JS code to work out this, so I haven't had a chance to test thoroughly. Please have a look and if you discover a flaw please mention.
In the below visual representation of the algorithm we try r = [1,3,1,3] and c = [3,2,1,2]. X denotes an occupied cell and a red dot denotes an untouchable cell while the empty ones are obviously the free cells. So in the real algorithm to represent a cell we need a data type like {value: false, avail: false} for a red dot while {value: false, avail: true} would mean a free space. Or to save space and speed you may use a data type like 0b00 for red dot, 0b01 for free space and 0b1X for occupied (X here means don't care) cells.
Note: It's worth mentioning Step 3 where we process c[0]. After we insert the three Xs we have to check the rows occupied by the Xs to update the status of the empty cells in those rows. In this case for r[2], all empty cells become untouchable.
Edit:
Well.. OK since we don't need to construct the solution in a 2D array like structure but only need an answer on wheather the supplied data is meaningful or not, I have come up with another and simpler idea which is essentially based on the above approach. I really don't think it can get any faster than this. It solves a 999 by 1000 board in like 50ms.
Lets get into it.
The input is r = [2, 3, 2]; c = [1, 1, 3, 2]; However one important condition here is both c and r arrays should sum up to the same number. We can simply check this at the beginning of our code or leave it, go through the following steps and if they pass check only if c is full of 0s. The following code prefers the latter approach.
Sort r descending so; r = [3, 2, 2]; c = [1, 1, 3, 2];
Try reducing r[0] (3 in the first case) many non-zero elements of c by 1. Now c becomes [0, 0, 2, 2]. If it fails then try no more and return false.
Now that we have finished with row r[0], recursivelly call function with r = [2, 2]; c = [0, 0, 2, 2]; while r.length is bigger than 0 and the bool argument b is true. Next call will be r = [2]; c = [0, 0, 1, 1]; and finally r = []; c = [0, 0, 0, 0];
If finally a recursive call with empty r is invoked then check b is true and all items of c are 0. (b && cs.every(n => !n)).
I believe this is just fine but as i don't have your test cases it's for you to try. I am sure it will pass the time test though. Here is the code in it's simplest. Here i am testing rs = [7,3,5,4,6,2,8] and cs = [7,1,6,3,4,5,2,7]. It looks like;
71634527
7 x xxxxxx
3 x x x
5 x x xx x
4 x x x x
6 x xxxx x
2 x x
8 xxxxxxxx
function nonogram(rs,cs){
function runner(rs,cs, b = true){//console.log(rs,cs,b)
return b && rs.length ? runner(rs.slice(1), // rows argument
cs.map(e => rs[0] ? e ? (b = !--rs[0], e-1) // cols argument
: e
: e),
b) // bool argument
: b && cs.every(n => !n);
}
return runner(rs.sort((a,b) => b-a), cs);
}
var rs = [7,3,5,4,6,2,8],
cs = [7,1,6,3,4,5,2,7],
result;
console.time("test");
result = nonogram(rs,cs);
console.timeEnd("test");
console.log(result);
I didn't have this ready for my test, but I found a far more efficient algorithm after the event.
'use strict';
const sortNumber = function (a, b) {
return b - a;
};
const isSolvable = function (r, c) {
const m = r.length;
const n = c.length;
if (m < 1 || n > 1000) {
throw new Error('Bad data');
}
for (let j = n; j >= 0; j -= 1) {
const cj = c[j];
if (cj < 0 || cj > m) {
throw new RangeError('cj out of range');
}
}
while (r.length) {
c.sort(sortNumber);
const ri = r.pop();
if (ri < 0 || ri > n) {
throw new RangeError('ri out of range');
}
if (ri) {
if (!c[ri - 1]) {
return 'No';
}
for (let j = ri - 1; j >= 0; j -= 1) {
c[j] -= 1;
}
}
}
for (let j = n - 1; j >= 0; j -= 1) {
if (c[j]) {
return 'No';
}
}
return 'Yes';
};
console.log(isSolvable([2, 3, 2], [1, 1, 3, 2]));
console.log(isSolvable([0, 0, 3], [0, 0, 3]));

Step through K combinations algorithm X steps at a time

I have a function (from Akseli Palén) to calculate combinations of X elements in a given Array that looks like this.
function k_combinations(set, k) {
var i, j, combs, head, tailcombs;
if (k > set.length || k <= 0) { return []; }
if (k == set.length) { return [set]; }
if (k == 1) {
combs = [];
for (i = 0; i < set.length; i++) { combs.push([set[i]]); }
return combs;
}
// Assert {1 < k < set.length}
combs = [];
for (i = 0; i < set.length - k + 1; i++) {
head = set.slice(i, i+1);
tailcombs = k_combinations(set.slice(i + 1), k - 1);
for (j = 0; j < tailcombs.length; j++) {
combs.push(head.concat(tailcombs[j]));
}
}
return combs;
}
It works nicely but when given a large array and/or K value to work with it slows down to the point where the browser tries to stop the script for being unresponsive so what I'm wondering is is it possible to extend this function so it accepts a starting position and maximum number of results to return in one call? That way I could do something like "Displaying results 20-30 out of 100,000". There is a sample form at
http://jsbin.com/ricabomofetu/1/edit?js,output if anyone wants to have a crack.
How about something like this?
function k_combinations(set, k, start_combo, max_results) {
if (start_combo.length !== k)
throw new Error("Starting combination is not of length k!");
var cur = [];
for (var i = 0; i < k; i++) {
var idx = set.indexOf(start_combo[i]);
if (idx === -1)
throw new Error(i + "th element of starting combination isn't in the set!");
if (i > 0 && idx <= cur[i - 1])
throw new Error("Elements of start_combo must be in sorted order!");
cur.push(idx);
}
function subset_from_indices(subset) {
var ret = [];
for (var i = 0; i < subset.length; i++)
ret.push(set[subset[i]]);
return ret;
}
var n = set.length;
var results = [subset_from_indices(cur)];
while (results.length < max_results) {
var inc_idx = k - 1;
while (inc_idx >= 0 && cur[inc_idx] === n - k + inc_idx)
inc_idx--;
if (inc_idx < 0) // no more combinations
return results
cur[inc_idx]++;
for (var i = inc_idx + 1; i < k; i++) {
cur[i] = cur[i - 1] + 1;
}
results.push(subset_from_indices(cur));
}
return results;
}
console.log(k_combinations([1, 2, 3, 4, 5], 3, [1, 3, 4], 4));
// [ [ 1, 3, 4 ], [ 1, 3, 5 ], [ 1, 4, 5 ], [ 2, 3, 4 ] ]

Categories