Step through K combinations algorithm X steps at a time - javascript

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 ] ]

Related

Storing instances of an array using the slice method in javascript

I have an array list I loop over and modify it each time. I want to store all instances of my list array in an other array I named allLists, and to do so, I'm using the slice method.
It seems to work in the simple example below:
let list=[1,2,3,4];
let allList = [];
allList.push(list.slice());
list[2]=6;
allList.push(list.slice());
console.log(allList);// returns [1,2,3,4] [1,2,6,4]
But it doesn't work in the following code. Instead, allLists is filled with the last instance of the list array.
let list = Array.from({
length: 9
}, () => Array.from({
length: 9
}, () => [1, 2, 3, 4, 5, 6, 7, 8, 9]));
let allLists = [list.slice()];
let indexList = [];
let lengthList = [];
let key = true;
function handleNewIndex(list) {
let newIndex = [0, 0];
let maxLength = 9;
for (let i = 0; i < list.length; i++) {
for (let j = 0; j < list.length; j++) {
if (list[i][j].length < maxLength && list[i][j].length > 0) {
maxLength = list[i][j].length;
newIndex = [i, j];
}
}
}
return newIndex;
}
function isSudokuValid(list) {
for (let i = 0; i < list.length; i++) {
for (let j = 0; j < list.length; j++) {
if (list[i][j].length === 0) {
return false;
}
}
}
return true;
}
function handleWrongSudoku(allLists, indexList, lengthList) {
let counter = 1;
while (lengthList[lengthList.length - counter] <= 1) {
counter = counter + 1;
allLists.pop();
indexList.pop();
}
let wrongList = allLists.pop();
list = allLists.pop();
indexLine = indexList[indexList.length - 1][0];
indexColumn = indexList[indexList.length - 1][1];
let wrongNumber = wrongList[indexLine][indexColumn];
for (let i = 0; i < list[indexLine][indexColumn].length; i++) {
if (list[indexLine][indexColumn][i] != wrongNumber) {
list[indexLine][indexColumn] = list[indexLine][indexColumn][i];
}
}
allLists.push(list.slice());
indexLine = handleNewIndex(list)[0];
indexColumn = handleNewIndex(list)[1];
}
function generateSudoku() {
let indexLine = Math.floor(Math.random() * 9);
let indexColumn = Math.floor(Math.random() * 9);
let counter = 0;
while (counter < 81) {
indexList.push([indexLine, indexColumn]);
let bigSquareIndex = 3 * Math.floor(indexLine / 3) + Math.floor(indexColumn / 3);
lengthList.push(list[indexLine][indexColumn].length);
list[indexLine][indexColumn] = list[indexLine][indexColumn][Math.floor(Math.random() * list[indexLine][indexColumn].length)];
counter = counter + 1;
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
if (3 * Math.floor(i / 3) + Math.floor(j / 3) === bigSquareIndex) {
let k = 0;
let n = list[i][j].length;
while (list[i][j][k] != list[indexLine][indexColumn] && k < n) {
k = k + 1;
}
if (k < n) {
list[i][j].splice(k, 1);
}
} else if (i === indexLine || j === indexColumn) {
let k = 0;
let n = list[i][j].length;
while (list[i][j][k] != list[indexLine][indexColumn] && k < n) {
k = k + 1;
}
if (k < n) {
list[i][j].splice(k, 1);
}
}
}
}
allLists.push(list.slice());
key = isSudokuValid(list);
if (key === false) { //ignore this scenario, not done yet, assume key = true at all time
console.log(key, lengthList, indexList, allLists);
handleWrongSudoku(allLists, indexList, lengthList);
key = true;
//return;
} else {
indexLine = handleNewIndex(list)[0];
indexColumn = handleNewIndex(list)[1];
}
}
}
generateSudoku();
console.log(allLists); // returns 81 times the same 9x9 array instead of the 81 different instances of the list array
I don't know what I'm doing wrong here.
Thanks for the hint Code Maniac.
I used the JSON method instead to create a deep copy and it's working fine now.
allLists.push(JSON.parse(JSON.stringify(list)));
This post explains the difference between shallow and deep copy:
https://www.freecodecamp.org/news/how-to-clone-an-array-in-javascript-1d3183468f6a/

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?

Unable to push a function return in a loop

I have this function which returns an array of prime numbers:
function getPrimeFactors(n) {
var factors = [];
for (i = 2; i <= Math.sqrt(n); i++) {
if (n % i === 0) {
var count = 0;
while (n % i === 0) {
n = n / i;
count++;
}
for (j = 1; j <= count; j++) {
factors.push(i);
}
}
}
if (n !== 1) {
factors.push(n);
}
return factors;
}
var numbers = [2, 3, 4, 5];
var array = [];
I want to get prime factors of all the numbers in the numbers array and push it into a new array (array)
for(i = 0; i < numbers.length; i++) {
array.push(getPrimeFactors(numbers[i]));
}
What am i doing wrong?
As mentioned by Damien Gold in the answer that was deleted, use var in your for-loops to make the variables local and to prevent your 2 functions from interfering with each other.
function getPrimeFactors(n) {
var factors = [];
for (var i = 2; i <= Math.sqrt(n); i++) {
if (n % i === 0) {
var count = 0;
while (n % i === 0) {
n = n / i;
count++;
}
for (var j = 1; j <= count; j++) {
factors.push(i);
}
}
}
if (n !== 1) {
factors.push(n);
}
return factors;
}
And:
var numbers = [2, 3, 4, 5];
var array = [];
for(var i = 0; i < numbers.length; i++) {
array.push(getPrimeFactors(numbers[i]));
}
As a sidenote,
you are pushing arrays into your array instead of adding the values contained in your getPrimeFactors-array. If you instead want to push the resulting numbers into one array, try this:
for(var i=0; i<numbers.length; i++) {
array = array.concat(getPrimeFactors(numbers[i]));
}

Optimisation pairs from array, sum of the pairs is Random value in JavaScript

I tried below code for print the pairs from array, the pairs sum is random value. but is there any other way to optimisation in JS?
var array = [1,24,10,12,11,13,23,9,15];r
var value = 24;
function myMthod(array,value){
var a;
var b;
for (var i=0; i < array.length; i++) {
a = array[i];
for (var j=0; j < array.length; j++) {
b = array[j]
if ( (parseInt(a) + parseInt(b)) === value && result.indexOf(a+","+b) == -1 && result.indexOf(b+","+a ) == -1 ) {
result.push( a+","+b )
}
}
}
return result
};
myMthod(array,value);
Is there any way to provide the optimisation? Thanks in advance!
There's no need for the second loop, when you got an item, just check if there's value - item in the array:
function pairs(array, value) {
var rs = [];
array.forEach(function(item, index) {
if (array.includes(value - item, index))
rs.push([item, value - item]);
});
return rs;
}
var array = [1,24,10,12,11,13,23,9,15];
var value = 24;
console.log(pairs(array, value))
First you need to make the code working. You need to change the 100 to value
if ( (parseInt(a) + parseInt(b)) === value && result.indexOf(a+","+b) == -1 && result.indexOf(b+","+a ) == -1 ) {
// ^^^^^
Better change it to a version without parseInt, because you work with numbers.
if (a + b === value && result.indexOf(a + "," + b) == -1 && result.indexOf(b + "," + a) == -1) {
// ^^^^^
var array = [1, 24, 10, 12, 11, 13, 23, 9, 15];
var value = 24;
function myMthod(array, value) {
var a, b, result = [];
for (var i = 0; i < array.length; i++) {
a = array[i];
for (var j = 0; j < array.length; j++) {
b = array[j];
if (a + b === value && result.indexOf(a + "," + b) == -1 && result.indexOf(b + "," + a) == -1) {
result.push(a + "," + b)
}
}
}
return result;
}
console.log(myMthod(array, value));
A slightly faster version could ignore the already looped items and work without a check for inserted items.
var array = [1, 24, 10, 12, 11, 13, 23, 9, 15];
var value = 24;
function myMthod(array, value) {
var a, b, i, j, result = [];
for (i = 0; i < array.length - 1; i++) {
a = array[i];
for (j = i + 1; j < array.length; j++) {
b = array[j];
if (a + b === value) {
result.push([a, b]);
}
}
}
return result;
}
console.log(myMthod(array, value));
.as-console-wrapper { max-height: 100% !important; top: 0; }

N nested iterations

I have an array with N objects
var test = [obj1, objc2, ..., objN]
The object is in this format
{"Key", "number", "Name" : "string", "OtherFields" : "Data"}
I want to iterate like this
for (var i = 0; i < test.length; i++) {
for (var j = 1; j < test.length; j++) {
for (var k = 2; k < test.length; k++) {
console.log(test[i].Name + test[j].Name + test[k].Name)
}
}
}
How can i achieve this result for n objects in the array ?
Example:
I have arrays [1, 2, 3, 4] , [2,4,5], [1,2]
i want to get this result:
1 2 1
1 2 2
1 4 1
1 4 2
1 5 1
1 5 2
.....
.....
4 5 2
Those arrays are not always 3 but can be any number >= 2
If I understand correctly, you want permutations.
Here is a recursive approach:
function perm(test, max_depth, depth, s){
if(depth === undefined)
depth = max_depth;
if(s === undefined)
s = '';
if(depth == 0)
console.log(s);
for(var i=depth; i<test.length;i++){
perm(test, max_depth, depth-1, s + test[i].Name);
}
}
var N = 10;
var n = 3;
var test = [];
for (var i = 0; i < N; i++) {
test.push({"Name" : "obj" + i});
}
function log_name_combinations(a, n) {
function nestedlog(a, maxlvl, lvl, s) {
if (lvl < maxlvl) {
for (var i = lvl; i < a.length; i++) {
nestedlog(a, maxlvl, lvl+1, s + a[i].Name);
}
} else {
console.log(s);
}
}
nestedlog(a, n, 0, "");
}
log_name_combinations(test, n);
Demo at JsFiddle
PS: Why are you starting the ith level iteration at the ith index?

Categories