Efficient algorithm to get the combinations of all items in object - javascript

Given an array or object with n keys, I need to find all combinations with length x.
Given X is variable. binomial_coefficient(n,x).
Currently I'm using this:
function combine(items) {
var result = [];
var f = function(prefix, items) {
for (var i = 0; i < items.length; i++) {
result.push(prefix + items[i]);
f(prefix + items[i], items.slice(i + 1));
}
}
f('', items);
return result;
}
var combinations = combine(["a", "b", "c", "d"]);
The output is:
["a", "ab", "abc", "abcd", "abd", "ac", "acd", "ad", "b", "bc", "bcd", "bd", "c", "cd", "d"]
So if I want the binomial coefficient x=3 from n=4 I select all the strings with length equal to three. {abc, abd, acd, bcd}.
So I do this in two steps.
Is there a more efficient algorithm with smaller complexity?
Link: Solution performance (JSPerf)

Your algorithm is almost O(2^n), you can discard a lot of combinations, but the num of elements will be (n! * (n-x)!) / x!.
To discard the useless combinations you can use an indexed array.
function combine(items, numSubItems) {
var result = [];
var indexes = new Array(numSubItems);
for (var i = 0 ; i < numSubItems; i++) {
indexes[i] = i;
}
while (indexes[0] < (items.length - numSubItems + 1)) {
var v = [];
for (var i = 0 ; i < numSubItems; i++) {
v.push(items[indexes[i]]);
}
result.push(v);
indexes[numSubItems - 1]++;
var l = numSubItems - 1; // reference always is the last position at beginning
while ( (indexes[numSubItems - 1] >= items.length) && (indexes[0] < items.length - numSubItems + 1)) {
l--; // the last position is reached
indexes[l]++;
for (var i = l +1 ; i < numSubItems; i++) {
indexes[i] = indexes[l] + (i - l);
}
}
}
return result;
}
var combinations = combine(["a", "b", "c", "d"], 3);
console.log(JSON.stringify(combinations));
For example, the first combination have the indexes: [0, 1, 2] and the elements ["a", "b", "c"]. To compute the next combination, It get the last index 2 and try to increment, if the increment is lower than the max position (in this case 4), the next combination is reached, but if It is not, It must increment to a previous index.

You could use an iterative and recursive approach with stress on the length of the array and the still needed items.
Basically combine() takes an array with the values to combine and a size of the wanted combination results sets.
The inner function c() takes an array of previously made combinations and a start value as index of the original array for combination. The return is an array with all made combinations.
The first call is allways c([], 0), because of an empty result array and a start index of 0.
function combine(array, size) {
function c(part, start) {
var result = [], i, l, p;
for (i = start, l = array.length; i < l; i++) {
p = part.slice(0); // get a copy of part
p.push(array[i]); // add the iterated element to p
if (p.length < size) { // test if recursion can go on
result = result.concat(c(p, i + 1)); // call c again & concat rresult
} else {
result.push(p); // push p to result, stop recursion
}
}
return result;
}
return c([], 0);
}
console.log(combine(["a", "b", "c", "d"], 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }

We could create just those combinations we are interested in. Also, rather than cloning arrays by using slice in each call, we can use a pointer to the original array. Here's one version. Converting it to recursion without an external global variable is left as an exercise.
function choose(ns,r){
var res = [];
function _choose(i,_res){
if (_res.length == r){
res.push(_res);
return;
} else if (_res.length + ns.length - i == r){
_res = _res.concat(ns.slice(i));
res.push(_res);
return
}
var temp = _res.slice();
temp.push(ns[i]);
_choose(i + 1,temp);
_choose(i + 1,_res);
}
_choose(0,[]);
return res;
}
var combinations = choose(["a", "b", "c", "d"], 3);
console.log(JSON.stringify(combinations));

And here's the true recursion.
function seq(a,b){
var res = [];
for (var i=a; i<=b; i++)
res.push(i);
return res;
}
function f(n,k){
if (k === 0)
return [[]];
if (n === k)
return [seq(1,n)];
let left = f(n - 1, k - 1),
right = f(n - 1, k);
for (let i=0; i<left.length; i++)
left[i].push(n);
return left.concat(right);
}
console.log(JSON.stringify(f(4,3)))

Related

Find the largest subarray length with delimiter

I'm trying to solve this problem:
Given two parameters: an array a and integer i, find the largest array length where the sum of all elements is <= i.
For example, having the following array: [3, 1, 2, 1], and i = 4, all the combinations that are <= i are: [3], [1], [2], [3, 1], [1, 2], [1, 2, 1]. The largest subarray is [1, 2, 1], so the return of the function should be 3 (the array length).
What would be a an efficient approach to solve this problem?
This is my algorithm until now, but I know that I'm missing something:
function sumArray(a) {
return a.reduce((a, b) => a + b, 0)
}
function maxLength(a, i) {
let max = 0
let array = [a[0]]
a.splice(1, a.length).forEach(number => {
array.push(number)
if (sumArray(array) <= k) max = array.length
else array.splice(array.indexOf(Math.max.apply(null, array)), 1)
})
return max
}
Here's how I would do it.
First, we'll get the longest sub-array by taking only the smallest elements out of the original, because that way the sum will be smallest possible as we go along. So:
const original = [3, 1, 2, 1];
const maxsum = 4;
// To make sure we take only the smallest, let's just
// sort the array straight away. That way the smallest
// element will always be the first.
// The .slice call is to make a copy, so we don't change
// the original:
const sorted = original.slice().sort();
// Here's your sum function. Looks legit.
function sumArray(a) {
return a.reduce((a, b) => a + b, 0)
}
// Now take items from the front of the sorted array and
// put them in the new array until either the original is
// empty or the max is reached.
let subarray = [];
while (sorted.length) {
// Get the next smallest element. It's always the
// first one because of the sort.
const smallest = sorted.shift();
// If the sum of what we have plus this new element
// is bigger than the max, our work is done:
if (sumArray(subarray) + smallest > maxsum) break;
// Otherwise, add it to our sub array and continue.
subarray.push(smallest)
}
// Once that loop has run, either we ran out of elements,
// or we hit the max. Either way, our job is done.
console.log("Original array:", original);
console.log("Maximal subset:", subarray);
console.log("Maximal subset length:", subarray.length);
Finally, if you want to get fancy, you can even do this with a single .reduce call:
const original = [3, 1, 2, 1];
const maxsum = 4;
const maximalSubset = original.slice().sort().reduce((subset, current) => {
if (subset.reduce((s, c) => s + c, 0) + current <= maxsum) subset.push(current);
return subset;
}, []);
console.log("Orignal:", original);
console.log("Maximal subset:", maximalSubset);
console.log("Maximal subset length:", maximalSubset.length);
Although while shorter, that second snippet has the downside that we have to iterate the entire array before we get the result, whereas the first one will stop once the maximum is reached.
EDIT:
It turns out that the subarray needs to be a continuous piece of the original, so changing the order of the original won't work since we need to make sure the result is a continuous slice of the original.
To do that, instead, just check each subslice of the array, and keep the best one:
let original = [74,659,931,273,545,879,924,710,441,166,493,43,988,504,328,730,841,613,304,170,710,158,561,934,100,279,817,336,98,827,513,268,811,634,980,150,580,822,968,673,394,337,486,746,229,92,195,358,2,154,709,945,669,491,125,197,531,904,723,667,550];
const maxsum = 22337;
function arraySum(arr) {
return arr.reduce((p, c) => p + c, 0);
}
// Double for loop will do the trick.
let bestSoFar = [];
for (let i = 0; i < original.length; i++) {
for (let j = i+1; j < original.length; j++) {
if (j-i > bestSoFar.length && arraySum(original.slice(i, j)) < maxsum) {
bestSoFar = original.slice(i, j);
}
}
}
console.log("Longest continuous subarray is:", bestSoFar.length);
A brute force approach is likely the best solution to this problem. Start at each entry and see how far you can go before arriving at a sum > i and if it's better than the best you've seen so far save it. I provided a sample Java solution below, I haven't actually run it so one or two of my indices may be off but I think you can get the gist. Runtime is O(n^2), memory is O(n) (both occurring with, for example, #getMaxSubArray(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 1000000))
private int[] getMaxSubArray(int[] a, int i) {
int minIndex = 0;
int maxIndex = -1;
for (int j = 0; j < a.length; j+=1) {
if (a[j] > i) {
continue;
}
int sum = a[j];
for (int k = j + 1; k < a.length; k++) {
if (sum + a[k] > i) {
if ((k - 1) - j > maxIndex - minIndex) {
maxIndex = k - 1;
minIndex = j;
}
break;
}
sum += a[k];
}
}
if (maxIndex - minIndex < 0) {
return null;
}
int[] result = new int[maxIndex - minIndex + 1];
for (int p = minIndex; p <= maxIndex; p += 1) {
result[p - minIndex] = a[p];
}
return result;
}
This is my solution. It will return the max length of the subarray. Can you please take a look, guys?
function maxLength(a, k) {
const sortedArray = a.sort((i,j) => i - j);
let sum = 0;
let length = 0;
const subArray = [];
for (let i=0; i < sortedArray.length; i++) {
sum = sum + sortedArray[i];
if (sum <= k) {
length++;
subArray.push(sortedArray[i]);
} else {
return length;
}
}
return length;
}

JavaScript: Convert a 1D array into a 2D array by a given size

Doing a freeCodeCamp challenge called "Chunky Monkey". The objective is to create a function that takes two parameters: a 1D array and a number for size.
The array is to be split into a number of groups of the length size (up to that number), thus creating a 2D array.
In my first attempt, my code was:
function chunkArrayInGroups(arr, size) {
var set = arr.length / size;
var count = 0;
set = Math.ceil(set); //ensure that an integer is obtained
var array = [];
for (var i = 0; i < set; i++) {
array[i] = []; //ensure each element i is an array
for (var j = 0; j < size; j++) {
array[i][j] = arr[count]; //obtain values from passed array, arr
count++;
}
}
return array;
}
var result = chunkArrayInGroups(["a", "b", "c", "d"], 2);
console.log(result);
I have traced this code a few times, but I cannot tell why it is wrong. Can someone please spot the error in it?
I eventually tackled the problem using a slightly different method, and it worked, but I am very interested in why the code above did not work.
You need to break the loop once count reach the max limit:
function chunkArrayInGroups(arr, size) {
var set = arr.length/size;
var count = 0;
set = Math.ceil(set); //ensure that an integer is obtained
var array = [];
out:
for(var i = 0; i<set; i++){
array[i] = []; //ensure each element i is an array
for (var j=0; j<size; j++){
if (count === arr.length) {
break out;
}
array[i][j] = arr[count]; //obtain values from passed array, arr
count++;
}
}
return array;
}
var result = chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 4);
console.log(result);
The code is working (with some exceptions, see result of console.log( chunkArrayInGroups(["a", "b", "c", "d"], 5) ); and #shaochuancs answer), but you may try a simpler approach utilizing native methods, like (see chunk function):
function chunkArrayInGroups(arr, size) {
var set = arr.length/size;
var count = 0;
set = Math.ceil(set); //ensure that an integer is obtained
var array = [];
for(var i = 0; i<set; i++){
array[i] = []; //ensure each element i is an array
for (var j=0; j<size; j++){
array[i][j] = arr[count]; //obtain values from passed array, arr
count++;
}
}
return array;
}
function chunk( arr, size ) {
var chunkedArray = [];
while ( arr.length > 0 ) {
chunkedArray.push( arr.splice( 0, size ) );
}
return chunkedArray;
}
console.log( chunkArrayInGroups(["a", "b", "c", "d"], 2) );
console.log( chunkArrayInGroups(["a", "b", "c", "d"], 5) );
console.log( chunkArrayInGroups([], 2) );
console.log( chunk(["a", "b", "c", "d"], 2) );
console.log( chunk(["a", "b", "c", "d"], 5) );
console.log( chunk([], 2) );

Find the N'th highest number in unsorted array

Today in a interview, I was told to write a program which will output the nth highest number in the unsorted array,
I solved this using javascript, the program is as follows,
var fn50 = function(){
var reverseSort = function(myArray,highest){
var x = 0,
y = 0,
z = 0,
temp = 0,
totalNum = myArray.length, // total numbers in array
flag = false, // is the numbers sorted in reverse while iteration
isAchieved = false; // whether we achieved the nth highest
while(x < totalNum){
y = x + 1; // start comparing 'yth' number which is next to 'xth' number.
if(y < totalNum){
// start comparing 'xth' with the next number, and if 'xth' number less than its next position number, just swipe them
for(z = y; z < totalNum; z++){
if(myArray[x] < myArray[z]){
temp = myArray[z];
myArray[z] = myArray[x];
myArray[x] = temp;
flag = true; // if number swiping done ?
}else{
continue;
}
}
}
if(flag){
flag = false;
}else{
x++; // x holds the max number in series, now move to next position to find next highest number
if(x > highest){ // if x is what the desired max number which we want flag it and break the loop to escape further iteration.
isAchieved = true;
}
}
if(isAchieved){
break;
}
}
print(myArray[(highest - 1)]);
};
reverseSort([12,56,78,34,11,100,95],4); // passing the unsorted array of number's, and finding the 4th highest number
};
fn50();
I got the desired output i.e the answer is 56 from the above array which is the 4th highest number.
But the interviewer told for a better solution.
Can you tell me or give me a hint, how can there be a better solution.
Some data structure technique ?
Sorting and selecting the kth highest number needs O(n log(n)) time, where n is the number of elements. In the bibliography, there is the medians of medians algorithm, that allows us to select the kth highest or smallest in linear time, no matter what value k has. You could find out if the interviewer had this kind of algorithm in mind, if you asked if the desired element could be the median of the array. The median is the element at position n / 2, which is considered the hardest case.
But for an interview, it's a complicated algorithm. If k is in general small, you can apply the following algorithm, based on the structure of a heap. You convert the array into a heap in linear time. Then you extract k times the largest element. This will take O(n + k * log(n)) time, which for small k = ο(n / log(n) is linear.
Having k be as small as a constant number, like 4, has an even simpler linear algorithm. Every time we scan the array and remove the largest. This will take O(k * n) time and because k is constant, O(k * n) = O(n).
I tried to implement this with quickselect as JuniorCompressor suggested.
But I wonder if that is really the fastest possible way. I guess the calculation of the pivot could be made more efficient.
var nthLargest = function(list, n) {
var i, a = 0, b = list.length, m, pivot;
if(n < 1) throw new Error("n too small");
if(list.length < n) throw new Error("n too large");
list = list.slice(0);
var swap = function(list, a, b) {
var temp = list[a];
list[a] = list[b];
list[b] = temp;
}
//returns the index of the first element in the right sublist
var partition = function(list, pivot, a, b) {
b--;
while(a <= b) {
if(list[a] <= pivot) a++;
else if(list[b] > pivot) b--;
else swap(list, a, b);
}
return a;
}
while(b - a > 1) {
for(i = a, pivot = 0; i < b; i++) {
pivot += list[i];
}
pivot /= b-a;
m = partition(list, pivot, a, b);
if(b - m >= n) a = m; // select right sublist
else { // select left sublist
if(m === b) return list[a]; // all elements in sublist are identical
n -= b - m;
b = m;
}
}
if(n !== 1) throw new Error();
return list[a];
}
<script>
function nthlargest(array, highest) {
array.sort();
l=array.length;
if(highest>l)
return("undefined");
else
return(array[l-highest+1]);
}
document.write(nthlargest([23, 652, 43, 89, 23, 90, 99, 88], 2));
</script>
Sorting is the simplest way I can think of.
But it appears you created your own sorting implementation.
Why not use the Array.sort function?
function nthHighest(numbers, n) {
var sorted = numbers.sort(function (a, b) {
return a - b;
});
return sorted[sorted.length - n];
}
You could simplify the arithmetic by doing a reverse sort, which just means b - a instead of a - b, then you don't need to pull from the back, which is just a cosmetic improvement.
function nthHighest(numbers, n) {
var sorted = numbers.sort(function (a, b) {
return b - a;
});
return sorted[n - 1];
}
You could also iterate over the array once, copying each element into a new array in sorted order and again, taking the Nth to last element, using underscore to implement a binary search.
function nthHighest(numbers, n) {
var sorted = [];
numbers.forEach(function (number) {
sorted.splice(_.sortedIndex(sorted, number), 0, number);
});
return sorted[numbers.length - n];
}
But this is basically a spin on the same concept: sort and take N. This approach would also perform better with a linked list than a pure array due to the restructuring, but that can be a separate exercise.
I came up with my own solution as it goes:
const nthlargest = (arr, n) => {
let newArr = [arr[0]];
for (let index = 1; index < arr.length; index++) {
const element = arr[index];
// push to end
if (element > newArr[index - 1]) {
newArr.push(element);
} else {
let insertPos = 0;
// if greater than first and less than last
if (newArr[0] < element && element < newArr[index - 1]) {
for (let j = 0; j < newArr.length; j++) {
if (newArr[j] > element) {
insertPos = j;
}
}
}
//insert at specified pos
newArr.splice(insertPos, 0, element);
}
}
return newArr[n];
}
console.log(nthlargest([43, 56, 23, 89, 88, 90, 99, 652], 4));
// counting from 0
// 89
This is without sorting the original array else it would be much easier.
using the sort array method
function nthLargest(array, n){
array.sort(function(a, b) {
return b - a; //organises the array in descending organises
});
let i = n - 1; //i is the index of the nth largest number
console.log(array[i]);
}
def funcc(arr, n):
max=0
k=0
while (n>0):
n-=1
for i in arr:
if (i>max):
max=i
if(n>0):
arr.remove(max)
max=0
return max
a = [1,2,3,4,19,10,5,11,22,8]
k = funcc(a, 3)
print(k)
const data = [30, 8, 2, 350, 4, 63, 98];
let max = 0;
var nth = 5;
for (let i = 0; i < nth; i++) {
max = 0;
for (let j = 0; j < data.length; j++) {
if (data[j] > max) {
max = data[j];
}
}
var ind = data.indexOf(max);
console.log(data);
const key = data.slice(ind, ind + 1);
}
console.log(nth + " largest number is ", max);

Implementing merge sort in Javascript

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)

Output each combination of an array of numbers with javascript

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.

Categories

Resources