Given N sorted arrays of integers (no duplicates), I'd like to calculate the first limit integers in their intersection.
For example, given the following arrays:
[2, 5, 7, 8, 10, 12, 13, 15, 20, 24]
[3, 4, 5, 6, 9, 10, 11, 17, 20]
[1, 2, 3, 5, 6, 10, 12, 20, 23, 29]
the intersection is [5, 10, 20], so if limit = 2, the result should be [5, 10].
The given arrays should not be mutated.
My attempt is below. Playground here.
Is there a more efficient (faster) way to achieve this?
Would appreciate a jsperf comparison.
function intersection(sortedArrays, limit) {
var arraysCount = sortedArrays.length;
var indices = sortedArrays.map(function(array) { return 0; });
var values, maxValue, valuesAreSame, reachedEnd, i, result = [];
while (true) {
reachedEnd = indices.some(function(index, i) {
return index === sortedArrays[i].length;
});
if (reachedEnd) {
return result;
}
values = sortedArrays.map(function(array, i) { return array[indices[i]]; });
valuesAreSame = values.every(function(value, i) { return value === values[0]; });
if (valuesAreSame) {
result[result.length] = values[0];
if (result.length === limit) {
return result;
}
for (i = 0; i < arraysCount; i++) {
indices[i]++;
}
} else {
maxValue = Math.max.apply(null, values);
for (i = 0; i < arraysCount; i++) {
if (values[i] < maxValue) {
indices[i]++;
}
}
}
}
}
console.log(intersection([[0, 3, 8, 11], [1, 3, 11, 15]], 1));
// => [3]
The first challenge is to make the function correct. Once it's correct, we can worry about the speed.
There are a few things which could trip-up a function like this:
NaN
Bad limits
Repeated numbers
Only 1 input array (or none at all)
Your original function can handle repeated numbers, such as [[9,9,9,9],[9,9,9]], but gets stuck in an infinite loop if any value is NaN, and handles a limit of 0 as if there were no limit at all.
Here's my (Mk3) attempt:
function intersection( arrs, limit ) {
var result = [], posns = [];
var j, v, next, n = arrs.length, count = 1;
if( !n || limit <= 0 ) {
return result; // nothing to do
}
if( n === 1 ) {
// special case needed because main loop cannot handle this
for( j = 0; j < arrs[0].length && result.length < limit; ++ j ) {
v = arrs[0][j];
if( v === v ) {
result.push( v );
}
}
return result;
}
for( j = 0; j < n; ++ j ) {
if( !arrs[j].length ) {
return result; // no intersection
}
posns[j] = 0;
}
next = arrs[n-1][0];
++ posns[n-1];
while( true ) {
for( j = 0; j < n; ++ j ) {
do {
if( posns[j] >= arrs[j].length ) {
return result; // ran out of values
}
v = arrs[j][posns[j]++];
} while( v < next || v !== v );
if( v !== next ) {
count = 1;
next = v;
} else if( (++ count) >= n ) {
result.push( next );
if( result.length >= limit ) {
return result; // limit reached
}
if( posns[j] >= arrs[j].length ) {
return result; // ran out of values
}
next = arrs[j][posns[j]++];
count = 1;
}
}
}
}
(fiddle: http://jsfiddle.net/kn2wz2sc/4/)
This works in much the same way as your original method, but with several optimisations. It always knows which number it is looking for next, and will quickly iterate through each array until it finds a number which is at least that big. If the number is too big, it will update the number it is looking for.
In Mk2 I took some inspiration from Casey's method of counting matches as it goes instead of checking from 0-n each time, which allows it to short-cut some comparisons (and since Casey is now using indices, both methods have become very similar). In Mk3 I've made some more micro-optimisations, incrementing the indexes eagerly so that it doesn't need an inner loop.
This is safe against all the criteria I listed above (it ignores NaN since NaN!=NaN and therefore will never be in the intersection), isn't limited to numbers, and will exit quickly once any limit is reached.
A jsperf shows that Mk3 is the fastest method so far: http://jsperf.com/sorted-intersect/5 (and it's still safe against duplicates and NaN).
Here's another algorithm, where the idea is that we count how many times we see each number. Once we see it arrs.length times, we know that it's in the intersection. If it's missing from even one list, it's not in the intersection, and we can skip to the next number in that list. It turns out to be a lot faster!
This method mutates the array, but is easier to read.
function intersection(arrs, limit) {
var intersections = [];
// Keep track of how many times we've seen the largest element seen so far.
var largest = -Infinity;
var count = 0;
while (intersections.length < limit) {
for (var i = 0; i < arrs.length; i++) {
// Drop elements less than `largest`.
while (arrs[i].length && arrs[i][0] < largest)
arrs[i].shift();
// Ignore repeated elements (not needed if you don't have repeated elements).
while (arrs[i].length >= 2 && arrs[i][0] == largest && arrs[i][1] == largest)
arrs[i].shift();
// If we ran out of elements, we're done.
if (!arrs[i].length)
return intersections;
// Look at the next element.
var next = arrs[i].shift();
if (next == largest)
count++;
else {
count = 1;
largest = next;
}
// Once we see it enough times, we can be sure it's in the intersection!
if (count == arrs.length)
intersections.push(largest);
}
}
return intersections;
}
This method doesn't, but it's harder to read.
function intersection(arrs, limit) {
var intersections = [];
var indices = [];
for (var i = 0; i < arrs.length; i++)
indices[i] = 0;
// Keep track of how many times we've seen the largest element seen so far.
var largest = -Infinity;
var count = 0;
while (intersections.length < limit) {
for (var i = 0; i < arrs.length; i++) {
// Skip past elements less than `largest`.
while (indices[i] < arrs[i].length && arrs[i][indices[i]] < largest)
indices[i]++;
// If we ran out of elements, we're done.
if (indices[i] >= arrs[i].length)
return intersections;
// Look at the next element.
var next = arrs[i][indices[i]++];
if (next == largest)
count++;
else {
count = 1;
largest = next;
}
// Once we see it enough times, we can be sure it's in the intersection!
if (count == arrs.length)
intersections.push(largest);
}
}
return intersections;
}
Faster (but by a long shot not as fast as the other answers):
function intersectMultiple(sortedArrays, limit) {
var set = {}, result = [],
a = sortedArrays.length,
l = Math.max.apply(null, sortedArrays.map(function (a) {
return a.length;
})), i, j, c = 0, val;
for (i = 0; i < l && c < limit; i++) {
for (j = 0; j < a && c < limit; j++) {
val = sortedArrays[j][i];
if (!set.hasOwnProperty(val)) set[val] = 0;
if (++set[val] === a) result[c++] = val;
}
};
return result;
}
and
var s = [
[2, 5, 7, 8, 10, 12, 13, 15, 20, 24],
[3, 4, 5, 6, 9, 10, 11, 17, 20],
[1, 2, 3, 5, 6, 10, 12, 20, 23, 29]
];
intersectMultiple(s, 2);
// [5, 10]
http://jsperf.com/intersect-multiple
Related
I want to rearrange the elements of an integer array based on the number of factors of natural numbers.
So for example my array input would be [6, 16, 7] and output should be [7, 6, 16] because the factors of numbers are 4, 5, and 1. What would be the best way to solve it, I am thinking about two for loops, but am not fully sure, how to execute it. Here's, what I have done by now.
var number;
var factors = 0;
var array = [];
while (number != 0) {
number = +prompt("Input a natural number:");
if (number != 0) {
array.push(number);
} else {
break;
}
}
for (var i = 0; i < array.length; i++) {
var a = numOfFactors(i);
for () {
if (numOfFactors(i) > numOfFactors(j)) {
}
}
}
function numOfFactors(a) {
for (var i = a; i >= 1; --i) {
if (a % i == 0 && i >> 0) {
factors++;
}
}
return factors;
}
numOfFactors(number);
console.log(factors);
console.log(array);
Thanks
I want to rearrange the array order with for loops if that would be possible
I have written an algorithm that returns the smallest common multiple of two integers that can be evenly divided by both, and can also be evenly divided by all sequential integers in the range between the initial two integers.
My algorithm works well for numbers where the smallest common multiple is a reasonably low number. For numbers where the smallest common multiple is a very large number (ex for [1,13] its 360360) my algorithm breaks because it is dependant on running more and more for loops.
I have posted my code below. It seems very long, but the problem section is near the beginning, and the rest of the code is added for context.
Is there a more efficient solution that running more and more loop iterations to return larger numbers?
// If mystery failure occurs, examine i & j loop limits.
function smallestCommons(arr) {
var firstArray = [];
var secondArray = [];
var commonNums = [];
var sequential = [];
// Sort the array from lowest to highest
arr.sort(function(a,b) {
return a-b;
});
// Assing arr values to vars so that original data stays intact
var array0 = arr[0];
var array1 = arr[1];
//console.log(arr);
// Find all multiples of both inputs.
// THIS IS THE PROBLEM SECTION
for (var i = 0; i < 1000000; i++) {
firstArray.push(arr[0] * (i+1));
}
for (var j = 0; j < 1000000; j++) {
secondArray.push(arr[1] * (j+1));
}
// THAT WAS THE PROBLEM SECTION
// Find common multiples.
for (var k = 0; k < firstArray.length; k++) {
for (var l = 0; l < secondArray.length; l++) {
if (firstArray[k] == secondArray[l]) {
commonNums.push(firstArray[k]);
} // End if.
} // End l loop.
} // End k loop.
// Find sequential numbers in range between input parameters.
for (var m = 0; m < array1; m++) {
if (array0 + 1 !== array1) {
sequential.push(array0 += 1);
} // End of if.
} // End of m loop.
// Find commonNums divisible by arr && sequential to produce a whole number.
// commonNums [ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75]
// arr [1,5]
// sequential [ 2, 3, 4 ]
for (var n = 0; n < commonNums.length; n++) {
var match = true;
for (var o = 0; o < sequential.length; o++) {
for (var p = 0; p < arr.length; p++) {
if (commonNums[n] % arr[p] !== 0 || commonNums[n] % sequential[o] !== 0) {
match = false;
}
}
}
if(match) {
console.log(commonNums[n]);
}
} // End of n loop.
} // End function.
smallestCommons([1,13]);
What you do does not make a lot of sense and is really hard to read. All you need to know to solve this problem is a little bit of math. And this math tells you that:
lcm(a, b) = a * b / gcd(a, b)
lcm(a,b,c) = lcm(a,lcm(b,c))
which translates into something like this:
function gcd(a, b){
while (b !== 0) {
var tmp = a;
a = b;
b = tmp % b;
}
return a
}
function lcm(a, b){
return a * b / gcd(a, b);
}
function lcm_arr(arr){
var res = 1;
for (var i = 0; i < arr.length; i++){
res = lcm(res, arr[i]);
}
return res;
}
which gives you your results in less than a second:
lcm_arr([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
Sounds like a good place to use recursion, because you have a solid base case when the number difference is one (in your example smallestCommons([12,13]); or smallestCommons([1,2]); depending on the direction), and the rest (sequentials) would be incrementing the smaller or decrementing the bigger number in recursive calls. That could save you quite a few CPU cycles.
I'm trying to return the mode of an array. I have an inner loop and outer comparing each element to each other. Array = [5,3,6,3,3,3].
function mode(arr){
for (var i = 0; i < arr.length; i++) {
for (var k = i+1; k < arr.length; k++) {
if (arr[i] == arr[k]) {
modeItems += arr[k];
}else {
otherItems +=arr[i];
}
}
}return modeItems;
}
Result comes back "333333" instead of "3333". I see how this is happening on an excel sheet comparing the 15 total loops aar[i] and arr[k], but I'm not sure what to do.
Below is a hodgepodge answer. My confusion stems from the title
How to eliminate extra matches of element in single array
mixed with this part of the question
I'm trying to return the mode of an array.
Reducing an array to an array of single instances, and an array of all duplicates.
DEMO
var arra = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4];
function simplify(arr) {
var c = {}, o = [], d = [], i;
for (i = 0; i < arr.length; i++) {
if (!c.hasOwnProperty(arr[i])) {
c[arr[i]] = arr[i];
o.push(arr[i]);
} else {
d.push(arr[i]);
}
}
return {
reduced: o,
duplicates: d
};
}
console.log(simplify(arra));
// >> [object Object] {
// >> duplicates: [2, 3, 3, 4, 4, 4],
// >> reduced: [1, 2, 3, 4]
// >> }
Removing duplicates from an array in place.
DEMO
var arr = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4];
function elim (arra) {
var c = {}, i = 0;
while (i < arra.length) {
if (c[arra[i]]) {
arra.splice(i, 1);
} else {
c[arra[i]] = true;
i++;
}
}
return arra; // only required for debug.
}
console.log(elim(arr.slice()));
Looping that many times on an array just to find the mode is unnecessary. You can use an object literal as a cache to update your counts. If you want an array minus the mode (or an array with only the mode), you can use .filter afterwards.
Here's a basic implementation. We return null if there is no mode.
DEMO
function findMode (a) {
var cache = {},
len = a.length,
mode,
max = 0,
matched = false;
for (var i = 0; i < len; i++) {
cache[a[i]] = (cache[a[i]] + 1 || 1);
if (cache[a[i]] === max) {
matched = true;
} else if (cache[a[i]] > max) {
max = cache[a[i]];
mode = a[i];
matched = false;
}
}
return (matched ? null : mode);
}
var arr = [5,3,6,3,3,3],
myMode = findMode(arr),
filteredArr = arr.filter(function (e) {
return (e !== myMode);
}),
modeItems = arr.filter(function (e) {
return (e === myMode);
});
console.log(arr); // >> [5,3,6,3,3,3]
console.log(myMode); // >> 3
console.log(filteredArr); // >> [5, 6]
console.log(modeItems); // >> [3, 3, 3, 3]
No mode here:
findMode([5, 7, 5, 7]); // >> null
Something slightly more complex. Returns the mode, mode array, and inverse array in two passes of the original array.
DEMO
var findMode = (function () {
function getMode (a) {
var cache = {},
len = a.length,
mode,
max = 0,
matched = false;
for (var i = 0; i < len; i++) {
cache[a[i]] = (cache[a[i]] + 1 || 1);
if (cache[a[i]] === max) {
matched = true;
} else if (cache[a[i]] > max) {
max = cache[a[i]];
mode = a[i];
matched = false;
}
}
return (matched ? null : mode);
}
function split (arr, mode) {
var set = [],
inverse = [];
if (mode !== null) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === mode) {
set.push(arr[i]);
} else {
inverse.push(arr[i]);
}
}
}
return [set, inverse];
}
return (function (arr) {
var mode = getMode(arr),
lists = split(arr, mode);
return {
mode: mode,
set: lists[0],
inverse: lists[1]
};
});
}());
var info = findMode([5,3,6,3,3,3]),
noMode = findMode([5, 7, 5, 7]);
console.log(info);
console.log(noMode);
Maintain an object array for keeping processed items and ignore the item in the next iterator that are already processed.
function mode(arr) {
var processed = {};
for (var i = 0; i < arr.length; i++) {
for (var k = i + 1; k < arr.length; k++) {
if (arr[i] == arr[k]) {
modeItems += arr[k];
modeItems += !processed[arr[k]] ? arr[k] : '';
processed[arr[k]] = i;
break;
} else {
otherItems += arr[i];
}
}
}
return modeItems;
}
Output: 3,3,3,3
your code is working matching , for every number in the loop , the numbers after the index i+1 :
[5,3,6,3,3,3]
^ ^ //first match
[5,3,6,3,3,3]
^ ^ // match 2
[5,3,6,3,3,3]
^ ^ // match 3
[5,3,6,3,3,3]
^ ^ // match 4
[5,3,6,3,3,3]
^ ^ // match 5
[5,3,6,3,3,3]
^ ^ // match 6
a simple way to work around this issue would be to reset loop with continue if i has already been matched; but it would fail if you had multiple double occurrences..
Let me also add my two cents. This code delete matched items to don't use it again :)
function mode(arr){
var key = arr[0], // it wil be the mode, current value of array
count = 1, // it will be the count of mode value
icount;
for (var i = 0; i < arr.length-count+1; i++) { // there is no need to look any further -
// count is more then rest items
if (!arr[i]) continue;
icount = 1;
for (var k = i+1; k < arr.length; k++) {
if (!arr[k]) continue;
if (arr[i] == arr[k]) { icount++; delete arr[k]; }
}
if (icount > count) { count = icount; key = arr[i]; }
}
if (count == 1) ret = null; // At this moment key - mode. Below output
else { var ret = []; while(count--) ret[count] = key; ret = ret.join();}
return ret;
}
So I'm working on Khan Academy's Algorithms course, and am trying to implement a recursive merge sort in Javascript. Here is my code so far:
var mergeSort = function(array, p, r) {
if(r>p) {
var q = floor(r/2);
mergeSort(array, p, q);
mergeSort(array, q+1, r);
merge(array, p, q, r);
}
};
merge is a function provided by Khan Academy to merge the subarrays back together. It is giving me the error: 'Uncaught RangeError: Maximum call stack size exceeded'.
EDIT: More details: I am fairly sure the error is in my code, there code is purposefully obfuscated and unreadable because the user needs to implement it themselves in a later challenge.
Here is the code that actually calls the mergeSort function initially and declares the array:
var array = [14, 7, 3, 12, 9, 11, 6, 2];
mergeSort(array, 0, array.length-1);
println("Array after sorting: " + array);
Program.assertEqual(array, [2, 3, 6, 7, 9, 11, 12, 14]);
And here is the code for the merge function, although it is obfuscared as I mentioned above:
var merge = function(array, p, q, r) {
var a = [],
b = [],
c = p,
d, e;
for (d = 0; c <= q; d++, c++) {
a[d] = array[c];
}
for (e = 0; c <= r; e++, c++) {
b[e] = array[c];
}
c = p;
for (e = d = 0; d < a.length && e < b.length;) {
if (a[d] < b[e]) {
array[c] = a[d];
d++;
} else {
array[c] = b[e];
e++;
}
c++;
}
for (; d < a.length;) {
array[c] = a[d];
d++;
c++;
}
for (; e < b.length;) {
array[c] = b[e];
e++;
c++;
}
};
They also require my code inside of the mergeSort function be of the form:
if (____) {
var ____ = ____;
mergeSort(____,____,____);
mergeSort(____,____,____);
merge(____,____,____,____);
}
Mergesort is a divide and conquer algorithm which splits the range of indices to sort in two, sorts them separately, and then merges the results.
Therefore, middle variable should be the arithmetic mean of from and to, not the half of to.
I have renamed the variables to make it more understandable:
var mergeSort = function(array, from, to) {
if(to > from) {
var middle = Math.floor( (from+to)/2 ); // Arithmetic mean
mergeSort(array, from, middle);
mergeSort(array, middle+1, to);
merge(array, from, middle, to);
}
};
q is supposed to be the half way point between p and r, but you've failed to take into account that the starting point (i.e. p) might not be 0 when you do this:
var q = floor(r/2);
You need to do something like:
var q = floor((r-p)/2) + p;
Although as #Oriol points out the middle point is actually exactly the same as the arithmetic mean and so the calculation can be simplified.
Just for the sake of implementation of the merge sort in JS
function merge(arr){
if(arr.length <= 1) return arr;
let mid = Math.floor(arr.length/2);
let left = merge( arr.slice(0, mid));
let right = merge(arr.slice(mid))
function mergeSort(arr1, arr2) {
let result = [];
let i=0;
let j=0;
while(i< arr1.length && j < arr2.length) {
if(arr1[i] < arr2[j]){
result.push(arr1[i])
i++;
} else {
result.push(arr2[j])
j++;
}
}
while(i < arr1.length) {
result.push(arr1[i])
i++;
}
while(j < arr2.length){
result.push(arr2[j])
j++;
}
return result
}
return mergeSort(left,right)
}
console.log(merge([1,4,3,6,2,11,100,44]))
High level strategy
the merge sort works on the principle that it's better to sort two numbers than to sort a large list of numbers. So what it does is that it breaks down two lists into their individual numbers then compares them one to the other then building the list back up. Given a list of say 1,3,2, it'll split the list into 1 and 3,2 then compare 3 to 2 to get 2,3 and then compare the list of 2,3 to 1. If 1 is less than the first element in list of 2,3 it simply places 1 in front of the list. Just like that, the list of 1,3,2 is sorted into 1,2,3.
pseudocode-steps
1.take first member of first array
2.compare to first member of second array
3.if first member of first array is less than first member
of second array
4.put first member into sorted array
5.now merge first array minus first element
with second array
6.else take first member of second array
merge first array with remaining portion of second array
7.return sorted array
function mergesorted(list1, list2) {
let sorted = [];
if (list1.length === 0 || list2.length === 0) {
return list1.length === 0 ? list2.length === 0 ? [] : list2 : list1;
}
if (list1[0] < list2[0]) {
sorted.push(list1[0])
return sorted.concat(mergesorted(list1.slice(1), list2))
} else {
sorted.push(list2[0])
return sorted.concat(mergesorted(list1, list2.slice(1)))
}
}
console.log(mergesorted([1, 2], [3, 4])) //should: [1,2,3,4]
console.log(mergesorted([1,2], [3])) //should: [1,2,3]
Merge sort implementation, stable and in place
function sort(arr, start, end) {
if (start >= end-1) return;
var mid = start + ~~((end-start)/2);
// after calling this
// left half and right half are both sorted
sort(arr, start, mid);
sort(arr, mid, end);
/**
* Now we can do the merging
*/
// holding merged array
// size = end-start
// everything here will be copied back to original array
var cache = Array(end-start).fill(0);
var k=mid;
// this is O(n) to arr[start:end]
for (var i=start, r=0;i<mid;r++,i++) {
while (k<end && arr[k] < arr[i]) cache[r++] = arr[k++];
cache[r] = arr[i];
}
// k marks the position of the element in the right half that is bigger than all elements in the left
// effectively it tells that we should only copy start~start+k element from cache to nums
// because the rests are the same
for (var i=0;i<k-start;i++) arr[i+start]=cache[i];
}
A nice solution:
const merge = (left, right) => {
const resArr = [];
let leftIdx = 0;
let rightIdx = 0;
while (leftIdx < left.length && rightIdx < right.length) {
left[leftIdx] < right[rightIdx]
? resArr.push(left[leftIdx++])
: resArr.push(right[rightIdx++]);
}
return [...resArr, ...left.slice(leftIdx), ...right.slice(rightIdx)];
};
const mergeSort = arr =>
arr.length <= 1
? arr
: merge(
mergeSort(arr.slice(0, Math.floor(arr.length / 2))),
mergeSort(arr.slice(Math.floor(arr.length / 2)))
);
try this
const mergeTwoSortedArr = (arr1 = [], arr2 = []) => {
let i = 0,
j = 0,
sortedArr = [];
while(i < arr1.length || j < arr2.length) {
if(arr1[i] <= arr2[j] || (i < arr1.length && j >= arr2.length)) {
sortedArr.push(arr1[i]);
i++;
}
if(arr2[j] <= arr1[i] || (j < arr2.length && i >= arr1.length)) {
sortedArr.push(arr2[j]);
j++;
}
}
return sortedArr;
}
const mergeSort = (arr) => {
if(arr.length === 0 || arr.length === 1) {
return arr;
}
var mid = Math.floor(arr.length / 2);
var left = mergeSort(arr.slice(0, mid));
var right = mergeSort(arr.slice(mid));
return mergeTwoSortedArr(left, right);
}
when trying this
mergeSort([5, 2, 1, 4]) //[1, 2, 4, 5]
Time Complexity O(nlogn)
logn --> decomposition
n comaprison
Time complexity O(nlogn)
I have several numbers in an array
var numArr = [1, 3, 5, 9];
I want to cycle through that array and multiply every unique 3 number combination as follows:
1 * 3 * 5 =
1 * 3 * 9 =
1 * 5 * 9 =
3 * 5 * 9 =
Then return an array of all the calculations
var ansArr = [15,27,45,135];
Anyone have an elegant solution? Thanks in advance.
A general-purpose algorithm for generating combinations is as follows:
function combinations(numArr, choose, callback) {
var n = numArr.length;
var c = [];
var inner = function(start, choose_) {
if (choose_ == 0) {
callback(c);
} else {
for (var i = start; i <= n - choose_; ++i) {
c.push(numArr[i]);
inner(i + 1, choose_ - 1);
c.pop();
}
}
}
inner(0, choose);
}
In your case, you might call it like so:
function product(arr) {
p = 1;
for (var i in arr) {
p *= arr[i];
}
return p;
}
var ansArr = [];
combinations(
[1, 3, 5, 7, 9, 11], 3,
function output(arr) {
ansArr.push(product(arr));
});
document.write(ansArr);
...which, for the given input, yields this:
15,21,27,33,35,45,55,63,77,99,105,135,165,189,231,297,315,385,495,693
I think this should work:
var a = [1, 3, 5, 9];
var l = a.length;
var r = [];
for (var i = 0; i < l; ++i) {
for (var j = i + 1; j < l; ++j) {
for (var k = j + 1; k < l; ++k) {
r.push(a[i] * a[j] * a[k]);
}
}
}
Edit
Just for my own edification, I figured out a generic solution that uses loops instead of recursion. It's obvious downside is that it's longer thus slower to load or to read. On the other hand (at least on Firefox on my machine) it runs about twice as fast as the recursive version. However, I'd only recommend it if you're finding combinations for large sets, or finding combinations many times on the same page. Anyway, in case anybody's interested, here's what I came up with.
function combos(superset, size) {
var result = [];
if (superset.length < size) {return result;}
var done = false;
var current_combo, distance_back, new_last_index;
var indexes = [];
var indexes_last = size - 1;
var superset_last = superset.length - 1;
// initialize indexes to start with leftmost combo
for (var i = 0; i < size; ++i) {
indexes[i] = i;
}
while (!done) {
current_combo = [];
for (i = 0; i < size; ++i) {
current_combo.push(superset[indexes[i]]);
}
result.push(current_combo);
if (indexes[indexes_last] == superset_last) {
done = true;
for (i = indexes_last - 1; i > -1 ; --i) {
distance_back = indexes_last - i;
new_last_index = indexes[indexes_last - distance_back] + distance_back + 1;
if (new_last_index <= superset_last) {
indexes[indexes_last] = new_last_index;
done = false;
break;
}
}
if (!done) {
++indexes[indexes_last - distance_back];
--distance_back;
for (; distance_back; --distance_back) {
indexes[indexes_last - distance_back] = indexes[indexes_last - distance_back - 1] + 1;
}
}
}
else {++indexes[indexes_last]}
}
return result;
}
function products(sets) {
var result = [];
var len = sets.length;
var product;
for (var i = 0; i < len; ++i) {
product = 1;
inner_len = sets[i].length;
for (var j = 0; j < inner_len; ++j) {
product *= sets[i][j];
}
result.push(product);
}
return result;
}
console.log(products(combos([1, 3, 5, 7, 9, 11], 3)));
A recursive function to do this when you need to select k numbers among n numbers. Have not tested. Find if there is any bug and rectify it :-)
var result = [];
foo(arr, 0, 1, k, n); // initial call
function foo(arr, s, mul, k, n) {
if (k == 1) {
result.push(mul);
return;
}
var i;
for (i=s; i<=n-k; i++) {
foo(arr, i+1, mul*arr[i], k-1, n-i-1);
}
}
This is a recursive function.
First parameter is array arr.
Second parameter is integer s. Each call calculates values for part of the array starting from index s. Recursively I am increasing s and so array for each call is recursively becoming smaller.
Third parameter is the value that is being calculated recursively and is being passed in the recursive call. When k becomes 1, it gets added in the result array.
k in the size of combination desired. It decreases recursively and when becomes 1, output appended in result array.
n is size of array arr. Actually n = arr.length
var create3Combi = function(array) {
var result = [];
array.map(function(item1, index1) {
array.map(function(item2, index2) {
for (var i = index2 + 1; i < array.length; i++) {
var item3 = array[i];
if (item1 === item2 || item1 === item3 || item2 === item3 || index2 < index1) {
continue;
}
result.push([item1, item2, item3]);
}
});
});
return result;
};
var multiplyCombi = function(array) {
var multiply = function(a, b){
return a * b;
};
var result = array.map(function(item, index) {
return item.reduce(multiply);
});
return result;
}
var numArr = [1, 3, 5, 9];
// create unique 3 number combination
var combi = create3Combi(numArr); //[[1,3,5],[1,3,9],[1,5,9],[3,5,9]]
// multiply every combination
var multiplyResult = multiplyCombi(combi); //[15,27,45,135];
https://github.com/dankogai/js-combinatorics
Found this library. Tested to be working. Below is from the library document:
var Combinatorics = require('js-combinatorics');
var cmb = Combinatorics.combination(['a','b','c','d'], 2);
while(a = cmb.next()) console.log(a);
// ["a", "b"]
// ["a", "c"]
// ["a", "d"]
// ["b", "c"]
// ["b", "d"]
// ["c", "d"]
Using node, you can do this pretty easily using a library. First install bit-twiddle using npm:
npm install bit-twiddle
Then you can use it in your code like this:
//Assume n is the size of the set and k is the size of the combination
var nextCombination = require("bit-twiddle").nextCombination
for(var x=(1<<(k+1))-1; x<1<<n; x=nextCombination(x)) {
console.log(x.toString(2))
}
The variable x is a bit-vector where bit i is set if the ith element is contained in the combination.