two-dimensional 4x4 array with all row and column sums equal - javascript

I have an arrithmetic progression (array) of 16 odd numbers: 1,3,5,..,29,31.
I should put them into two-dimensional 4×4 array in such a way that sums of all of the rows and columns are the same. That would be number 64.
In how many ways can this be achieved?
Rotated or mirrored combinations are also considered as different ways.
The most naive way would be to permutate all array elements and check sums in rows and columns.
This job is similar to Magic Square problem, but diagonal sums don't have to be equal here.
What would be the most effective way to do this, preferably in JavaScript?
Example:
Input array:
1 3 5 7
9 11 13 15
17 19 21 23
25 27 29 31
One of the results:
1 11 21 31
29 23 9 3
27 5 19 13
7 25 15 17

Definitely not the fastest way, but it might be of use.
It takes about 200s to produce 549504 variations.
const startTime = Date.now();
const inputArray = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31];
const target = 64;
const combinationsOfFour = {};
//==================================================================
/*
Section.1
Find all valid combinations of four values from
the input array that total the target.
Store each combination in a nested object
grouped by the first value in the combination,
and referenced by a comma separated string of
the combination.
eg
look_up_table = {
1: {
'1,2,3,4': [1, 2, 3, 4],
'1,2,4,3': [1, 2, 4, 3],
...
},
2: {
...
}
}
*/
// --------------------------------------------
/*
Take groups of four ascending values, and find all
individual combinations, and assign them to their
corresponding group in the lookup table,
referenced by the comma separated string of their values
eg
abcd => 'a,b,c,d', 'a,b,d,c', 'a,c,b,d', 'a,c,d,b'...
*/
const assignAllVariants = groupA => {
groupA.forEach((valA, indexA) => {
const groupB = [...groupA];
groupB.splice(indexA, 1);
groupB.forEach((valB, indexB) => {
const groupC = [...groupB];
groupC.splice(indexB, 1);
groupC.forEach((valC, indexC) => {
const groupD = [...groupC];
groupD.splice(indexC, 1);
const valD = groupD[0];
const combination = [valA, valB, valC, valD];
combinationsOfFour[valA][combination.join(",")] = combination;
});
});
});
};
// ----------------------------------------------
/*
Get each combination of four ascending values that total the target
* Note - the input array would need an initial sort in cases where
it is not already in ascending order.
- loop through each value in the input array
- assign that value to 'first'
- beginning with the following value,
loop through each value in the array
- assign that value to 'second'
- beginning with the following value,
loop through each value in the array
- assign that value to 'third'
- subtract the sum of first, second and third
from the target
- assign this value to 'fourth'
- check that fourth is greater than third,
and less than or equal to the last value
in the array.
* Note if the input array is comprised of values with
some other spacing eg(1, 3, 6, 10, 15...)
then the value of 'fourth' would need to checked
against the array for validity
- All valid groups of four are passed to the
function assignAllVariants
*/
const createGroup = (target, values) => {
let first, second, third, fourth;
values.forEach(val => (combinationsOfFour[val] = {}));
return values.forEach((value, index) => {
first = value;
for (let i = index + 1; i < values.length - 2; i++) {
second = values[i];
for (let j = i + 1; j < values.length - 1; j++) {
third = values[j];
fourth = target - first - second - third;
if (fourth <= third) {
break;
}
if (fourth <= values[values.length - 1]) {
const group = [first, second, third, fourth];
assignAllVariants(group);
}
}
}
});
};
// ======================================================
/*
Section.2
- Loop through the values in the combinations table
created in section 1.
- Set the given combination to the first row of the grid.
- Remove the values of that combination from a lookup table
created from the input array.
- Taking the value in the first position of the first row,
- loop through the corresponding group in the combinations table
- Check that the combination does not contain values already removed
from the lookup table, or collide with existing values in the grid.
- Apply this combination to the first column of the grid.
- Repeat this process with the 2nd row, 2nd column, 3rd row...
- If the fourth row is successfully assigned then add that completed
grid to validLayouts
*/
const getGrid = (inputArray, combinations) => {
let grid = [[], [], [], []];
const validLayouts = [];
const gridToString = grid => {
return grid.map(row => row.join(",")).join("|");
};
// Check given combination against a lookup table of used/ unused values
const checkLookup = (combination, start, lookUp) => {
if (start > 0) {
for (let i = start; i < 4; i++) {
if (!lookUp[combination[i]]) {
return false;
}
}
return true;
} else {
return true;
}
};
// Check given combination against existing values in the grid
const checkAgainstGrid = (combination, n, axis) => {
if (axis === "x") {
if (n > 0) {
for (let i = 4 - n + 1; i < 4; i++) {
if (combination[4 - i] !== grid[n][4 - i]) {
return false;
}
}
return true;
} else {
return true;
}
} else if (axis === "y") {
for (let i = 4 - n; i < 4; i++) {
if (combination[4 - i] !== grid[4 - i][n]) {
return false;
}
}
return true;
}
};
// Update lookup table
const removeUsedValues = (combination, n, lookUp) => {
const newLookUp = { ...lookUp };
for (let i = n; i < 4; i++) {
newLookUp[combination[i]] = false;
}
return newLookUp;
};
// -----------------------------------
/*
Only needed when examining failed grid attempts,
can be removed, but minimal performance impact
on the given set.
*/
// Use to clear grid after unsuccessful branch
const cleanUpGrid = (n, axis) => {
if (axis == "x") {
grid[n].splice(n);
} else if (axis == "y") {
for (let i = n + 1; i < 4; i++) {
grid[i].splice(n);
}
}
};
// ------------------------------------------------
// Assign passing combinations to the corresponding grid column
const assignCol = (combination, n, lookUp) => {
let newLookUp;
// Check combination against lookup table and current grid values
if (
checkLookup(combination, n + 1, lookUp) &&
checkAgainstGrid(combination, n, "y")
) {
// remove used digits from lookup table
newLookUp = removeUsedValues(combination, n, lookUp);
// assign combination to column
for (let i = n + 1; i < 4; i++) {
grid[i][n] = combination[i];
}
Object.keys(combinations[grid[n + 1][0]]).forEach(ref => {
const combination = combinations[grid[n + 1][0]][ref];
assignRow(combination, n + 1, newLookUp);
});
cleanUpGrid(n, "y");
}
};
// Assign passing combinations to the corresponding grid row
const assignRow = (combination, n, lookUp) => {
// Check combination against lookup table and current grid values
let newLookUp;
if (
checkLookup(combination, n, lookUp) &&
checkAgainstGrid(combination, n, "x")
) {
// remove used digits from lookup table
newLookUp = removeUsedValues(combination, n, lookUp);
// assign combination to row
grid[n] = [...combination];
if (n === 3) {
validLayouts.push(gridToString(grid));
} else {
Object.keys(combinations[grid[0][n]]).forEach(ref => {
const combination = combinations[grid[0][n]][ref];
assignCol(combination, n, newLookUp);
});
cleanUpGrid(n, "x");
}
}
};
// create initial lookup table from input array
const lookUp = {};
inputArray.forEach(val => (lookUp[val] = true));
// main outer loop
Object.keys(combinations).forEach(group => {
Object.keys(combinations[group]).forEach(ref => {
const combination = combinations[group][ref];
assignRow(combination, 0, lookUp);
});
});
return validLayouts;
};
//-------------------------------------------------------
createGroup(target, inputArray);
const validGrids = getGrid(inputArray, combinationsOfFour);
console.log(validGrids.length);
console.log(`Duration: ${(Date.now() - startTime) / 1000}s`);

I can confirm the 549504 solutions. Code prints every 50000th of them, and your example, #4192 :-) (at least in my Chrome)
var total=0;
var start=Date.now();
function row(pos,field,rest){
var base=34;
for(var i=0;i<pos;i++)
base-=field[pos*4+i];
var ps=permutations(rest,3-pos);
for(var p of ps){
var r=base;
p.forEach(x=>r-=x);
if(rest.has(r) && !p.includes(r)){
for(var j=pos;j<3;j++)
field[pos*4+j]=p[j-pos];
field[pos*4+3]=r;
rest.delete(r);
p.forEach(x=>rest.delete(x));
column(pos,field,rest);
rest.add(r);
p.forEach(x=>rest.add(x));
}
}
}
function column(pos,field,rest){
if(rest.size===0){
total++;
if(total%50000===0 || total===4192)
console.log((" "+total).slice(-6),"["+field.map(x=>x*2-1).join()+"]");
return;
}
var base=34;
for(var i=0;i<pos+1;i++)
base-=field[pos+4*i];
var ps=permutations(rest,2-pos);
for(var p of ps){
var r=base;
p.forEach(x=>r-=x);
if(rest.has(r) && !p.includes(r)){
for(var j=pos+1;j<3;j++)
field[pos+j*4]=p[j-pos-1];
field[pos+4*3]=r;
rest.delete(r);
p.forEach(x=>rest.delete(x));
row(pos+1,field,rest);
rest.add(r);
p.forEach(x=>rest.add(x));
}
}
}
var items=new Set();
for(var i=0;i<16;i++)
items.add(i+1);
row(0,new Array(16).fill(0),items);
console.log("Total:",total);
console.log((Date.now()-start)/1000,"s");
function permutations(items,length){
if(length===0)
return [[]];
var elems=[...items];
var ret=[];
if(length===1)
for(var i=0;i<elems.length;i++)
ret.push([elems[i]]);
else if(length===2) {
for(var i=0;i<elems.length;i++)
for(var j=0;j<elems.length;j++)
if(i!==j)
ret.push([elems[i],elems[j]]);
} else if(length===3) {
for(var i=0;i<elems.length;i++)
for(var j=0;j<elems.length;j++)
if(i!==j)
for(var k=0;k<elems.length;k++)
if(k!==i && k!==j)
ret.push([elems[i],elems[j],elems[k]]);
}
return ret;
}
The base idea of the code is to have a (semi)complete field all the time, a set of the available (unused) numbers (rest), and fill up a row, then a column, then a row, etc.
The filling is the tricky part:
The sum of already present numbers is subtracted from the goal (that is base)
Permutations are generated using the available numbers, for the length covering the remaining part of the given row/column except for the last position in that row/column
The sum of a given permutation is subtracted from base, so the result (r) has to be last element. This result is checked for being available (which means being part of rest, and not being used by the given permutation), then the algoritm fills the given row/column, and proceeds with filling a column/row (providing an updated set of available numbers). Otherwise it just proceeds to the next permutation
When rest is empty (it will happen in column()), a solution is found.
Remarks
permutations() is ugly as hell, but it works
code is more readable now with Set operations, but actually it is 30-35% faster with pure arrays (splice()+indexOf() used for removing elements). We are talking about 5.5 vs 4 seconds, on a relatively new machine
internally it creates a 1...16 magic square, it converts to 1,3..31 when displaying it (addition/multiplication does not affect "magic square-ness")

Related

How to perform digit by digit multiplication in JS

I want to make a function which multiplies numbers digit by digit, like we did in school. For example, in 445 * 456, we first multiply 5 by 6, then, 4 and again 4. Then, we multiply 4 by 5 and so on. I want to do this for getting answers in strings for very very long multiplications.
Here, numArray is digits stored in array, e.g 563 is [5, 6, 3];
Similarily, another number can be e.g. 621 which is converted into [6, 2, 1]
for(var i = 1; i <= anotherNumArray.length; i++) {
var multiplier = anotherNumArray[anotherNumArray.length-i]
for(var j = 1; j <= numArray.length; j++) {
var multiplicand = numArray[numArray.length-j]
answer.unshift(multiplicand*multiplier);
}
I am trying to loop multiplication of numbers. But I am getting weird results.
the problem is more complicated than presented
assuming actual "long multiplication" simulation, i.e only digits multiplication and summation allowed, see below comments and working code.
first of all, the code is ignoring the fact that multiplications of two digits will overflow and will yield a non-zero carry value that should be summed with the next multiplication
secondly, as we've been tought in school, for each digit, we recieve a new multiplication result with all other digits of the second number. we will need to sum over those multiplication results in the end
code below works for non-negative decimal numbers. it could be simplified, but have tried to keep the spirit of the initial algorithm
// parameters
const lhs = "445";
const rhs = "456";
// utilities
const createZeros = length => new Array(length).fill(0);
const padArrayEnd = (length, padding, array) => [...array, ...createZeros(length - array.length)];
const last = array => array[array.length - 1];
// given a carray and an array of digits, add the carry to the last digit.
// if the result overflows, add the remainder as a new digit instead
// example;
// array = [3, 4, 5]; addCarry(2, array); array == [3, 4, 7]
// array = [3, 4, 9]; addCarry(2, array); array == [3, 4, 0, 1]
function addCarry(carry, digits) {
if (carry == 0) {
return;
}
let value = last(digits) + carry;
if (value > 10) {
digits[digits.length - 1] = 0;
digits.unshift(value % 10);
} else {
digits[digits.length - 1] = value;
}
}
console.log({ message: "start", lhs, rhs });
// state
const answer = [];
const entries = [];
let carry = 0;
// perform digit by digit multiplication.
// remember that the result array for each digit should have N leading zeros, where N is the position of that digit.
// this is the reason we keep an array of entries. each entry, is the result array corresponding to that digit
for(let lcur = 0; lcur < lhs.length; lcur++) {
const leftDigit = lhs[lhs.length - 1 - lcur];
// the multiplications entry
const multiplications = createZeros(lcur);
for(let rcur = 0; rcur < rhs.length; rcur++) {
const rightDigit = rhs[rhs.length - 1 - rcur];
// perform multiplication, but notice that in case of overflow we keep
// only the ones, and remember carry for next iteration
const times = (leftDigit * rightDigit) + carry;
const ones = times % 10;
carry = Math.floor(times / 10);
multiplications.unshift(ones);
console.log({ message: "step", expr: `${leftDigit} * ${rightDigit}`, times, ones, carry });
}
if (carry != 0){
multiplications.unshift(carry);
carry = 0;
}
// update entries
entries.push(multiplications);
console.log({ message: "entry", multiplications });
}
// add the final carry
addCarry(carry, last(entries));
console.log({ message: "entries", entries });
// sum all entries with carries
const maxLength = entries
.map(entry => entry.length)
.reduce((acc, entry) => Math.max(acc, entry), 0);
// for convinience, reverse all entries - this can by bypassed with messing around with indices
entries.forEach(entry => entry.reverse());
carry = 0;
for (let idx = 0; idx < maxLength; ++idx) {
const sum = entries
.map(entry => entry[idx] || 0)
.reduce((acc, value) => acc + value, carry);
const ones = sum % 10;
carry = Math.floor(sum / 10);
answer.unshift(ones);
console.log({ message: "summation", sum, ones, carry, answer });
}
// add final summation carry
// remember that we reversed stuff before, reverse back
// answer.reverse()
addCarry(carry, answer);
// finalize a result
const result = answer.join("");
const expected = (parseInt(lhs) * parseInt(rhs)).toString(); // string for some reason
console.log({ message: "finish", expr: `${lhs} * ${rhs} = ${answer.join("")}`, answer, expected });
If I understood your question correctly, you want an algorithm that multiplies two n-digit numbers using the Long multiplication method that most of us were taught in school.
Here is my approach (excessively commented so you understand what it does):
var n1 = [5, 6, 3]; // multiplicand
var n2 = [6, 2, 1]; // multiplier
var expectedResult = parseInt(n1.join('')) * parseInt(n2.join(''));
console.log('expectedResult = ' + expectedResult);
var partialProducts = [];
var product = '';
// Iterate over the multiplier from right to left
for (var i = n2.length - 1; i >= 0; i--) {
var d2 = n2[i];
var carry = 0;
// Iterate over the multiplicand from right to left
for (var j = n1.length - 1; j >= 0; j--) {
var d1 = n1[j];
// Perform the multiplication
var mult = (d2 * d1) + carry;
// Recalculate the carry for the next iteration
carry = Math.floor(mult / 10);
// add the last number of the multiplication
product = (mult % 10) + product;
}
// Add the remaining carry in case theres some left
product = (carry % 10) + product;
// Add a new product to the partialProducts array
// shifted the needed places to the left
var shift = (10 ** (n2.length - i - 1));
partialProducts.push(product * shift);
// A new product was pushed, clear it for the next iteration
product = '';
}
// Finally, just sumate all the partial products
var result = partialProducts.reduce((a, c) => a + c);
console.log('result = ' + result);

Checking whether the number of unique numbers within array exceeds n

Just as title reads, I need to check whether the number of unique entries within array exceeds n.
Array.prototype.some() seems to fit perfectly here, as it will stop cycling through the array right at the moment, positive answer is found, so, please, do not suggest the methods that filter out non-unique records and measure the length of resulting dataset as performance matters here.
So far, I use the following code, to check if there's more than n=2 unique numbers:
const res = [1,1,2,1,1,3,1,1,4,1].some((e,_,s,n=2) => s.indexOf(e) != s.lastIndexOf(e) ? false : n-- ? false : true);
console.log(res);
.as-console-wrapper { min-height: 100%}
And it returns false while there's, obviously 3 unique numbers (2,3,4).
Your help to figure out what's my (stupid) mistake here is much appreciated.
p.s. I'm looking for a pure JS solution
You can use a Map() with array values as map keys and count as values. Then iterate over map values to find the count of unique numbers. If count exceeds the limit return true, if not return false.
Time complexity is O(n). It can't get better than O(n) because every number in the array must be visited to find the count of unique numbers.
var data = [1, 1, 2, 1, 1, 3, 1, 1, 4, 1];
function exceedsUniqueLimit(limit) {
var map = new Map();
for (let value of data) {
const count = map.get(value);
if (count) {
map.set(value, count + 1);
} else {
map.set(value, 1);
}
}
var uniqueNumbers = 0;
for (let count of map.values()) {
if (count === 1) {
uniqueNumbers++;
}
if (uniqueNumbers > limit) {
return true;
}
}
return false;
}
console.log(exceedsUniqueLimit(2));
To know if a value is unique or duplicate, the whole array needs to be scanned at least once (Well, on a very large array there could be a test to see how many elements there is left to scan, but the overhead for this kind of test will make it slower)
This version uses two Set
function uniqueLimit(data,limit) {
let
dup = new Set(),
unique = new Set(),
value = null;
for (let i = 0, len = data.length; i < len; ++i) {
value = data[i];
if ( dup.has(value) ) continue;
if ( unique.has(value) ) {
dup.add(value);
unique.delete(value);
continue;
}
unique.add(value);
}
return unique.size > limit;
}
I also tried this version, using arrays:
function uniqueLimit(data, limit) {
let unique=[], dup = [];
for (let idx = 0, len = data.length; idx < len; ++idx) {
const value = data[idx];
if ( dup.indexOf(value) >= 0 ) continue;
const pos = unique.indexOf(value); // get position of value
if ( pos >= 0 ) {
unique.splice(pos,1); // remove value
dup.push(value);
continue;
}
unique.push(value);
}
return unique.length > limit;
};
I tested several of the solutions in this thread, and you can find the result here. If there are only a few unique values, the method by using arrays is the fastest, but if there are many unique values it quickly becomes the slowest, and on large arrays slowest by several magnitudes.
More profiling
I did some more tests with node v12.10.0. The results are normalized after the fastest method for each test.
Worst case scenario: 1000000 entries, all unique:
Set 1.00 // See this answer
Map 1.26 // See answer by Nikhil
Reduce 1.44 // See answer by Bali Balo
Array Infinity // See this answer
Best case scenario: 1000000 entries, all the same:
Array 1.00
Set 1.16
Map 2.60
Reduce 3.43
Question test case: [1, 1, 2, 1, 1, 3, 1, 1, 4, 1]
Array 1.00
Map 1.29
Set 1.47
Reduce 4.25
Another test case: [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,
1,1,1,1,1,1,1,3,4,1,1,1,1,1,1,1,2,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,5 ]
Array 1.00
Set 1.13
Map 2.24
Reduce 2.39
Conclusion
The method that uses Set works for both small and large arrays, and performs well regardless of if there are many unique values or not. The version that are using arrays can be faster if there are few unique values, but quickly becomes very slow if there are many unique values.
Using sets, We count hypothetical unique set size and duplicateSet size and delete unique set element for each duplicate found. If unique set size goes below n, we stop iterating.
function uniqueGtN(res, n) {
let uniqSet = new Set(res);
let max = uniqSet.size;
if (max <= n) return false;
let dupSet = new Set();
return !res.some(e => {
if (dupSet.has(e)) {
if (uniqSet.has(e)) {
uniqSet.delete(e);
console.log(...uniqSet);
return (--max <= n);
}
} else {
dupSet.add(e);
}
});
}
console.log(uniqueGtN([1, 1, 2, 1, 1, 3, 3, 1], 2));
From your original solution, I have changed few things, it seems to be working fine:
(function() {
const array = [1,1,2,1,1,3,1,1,4,1];
function hasExceedingUniqueNumber(array, number) {
return array.some((e,_,s,n=number) => {
let firstIndex = s.indexOf(e);
let lastIndex = s.lastIndexOf(e);
// NOT unique
if (firstIndex != lastIndex) {
return false;
}
// unique
return e > n;
});
}
console.log('1', hasExceedingUniqueNumber(array, 1));
console.log('2', hasExceedingUniqueNumber(array, 2));
console.log('3', hasExceedingUniqueNumber(array, 3));
console.log('4', hasExceedingUniqueNumber(array, 4));
})();
So the shorter version looks like this:
(function() {
const array = [1,1,2,1,1,3,1,1,4,1];
function hasExceedingUniqueNumber(array, number) {
return array.some((e,_,s,n=number) => s.indexOf(e) != s.lastIndexOf(e) ? false : e > n);
}
console.log('1', hasExceedingUniqueNumber(array, 1));
console.log('2', hasExceedingUniqueNumber(array, 2));
console.log('3', hasExceedingUniqueNumber(array, 3));
console.log('4', hasExceedingUniqueNumber(array, 4));
})();
The code listed in your question does not work because m is not shared across the calls to the some callback function. It is a parameter, and its value is 2 at each iteration.
To fix this, either put m outside, or use the thisArg of the some function (but that means you can't use an arrow function)
let m = 2;
const res = [1,1,1,2,1,1,3,1,1,1,4,1,1]
.sort((a,b) => a-b)
.some((n,i,s) => i > 0 && n == s[i-1] ? !(m--) : false);
// ----- or -----
const res = [1,1,1,2,1,1,3,1,1,1,4,1,1]
.sort((a,b) => a-b)
.some(function(n,i,s) { return i > 0 && n == s[i-1] ? !(this.m--) : false; }, { m: 2 });
Note: this code seems to count if the number of duplicates exceeds a certain value, not the number of unique values.
As another side note, I know you mentioned you did not want to use a duplicate removal algorithm, but performant ones (for example hash-based) would result in something close to O(n).
Here is a solution to count all the values appearing exactly once in the initial array. It is a bit obfuscated and hard to read, but you seem to be wanting something concise. It is the most performant I can think of, using 2 objects to store values seen at least once and the ones seen multiple times:
let res = [1,1,2,3,4].reduce((l, e) => (l[+!l[1][e]][e] = true, l), [{},{}]).map(o => Object.keys(o).length).reduce((more,once) => once-more) > 2;
Here is the less minified version for people who don't like the short version:
let array = [1,1,2,3,4];
let counts = array.reduce((counts, element) => {
if (!counts.atLeastOne[element]) {
counts.atLeastOne[element] = true;
} else {
counts.moreThanOne[element] = true;
}
return counts;
}, { atLeastOne: {}, moreThanOne: {} });
let exactlyOnceCount = Object.keys(counts.atLeastOne).length - Object.keys(counts.moreThanOne).length;
let isOverLimit = exactlyOnceCount > 2;
Whenever I have a type of problem like this, I always like to peek at how the underscore JS folks have done it.
[Ed again: removed _.countBy as it isn't relevant to the answer]
Use the _.uniq function to return a list of unique values in the array:
var u = _.uniq([1,1,2,2,2,3,4,5,5]); // [1,2,3,4,5]
if (u.length > n) { ...};
[ed:] Here's how we might use that implementation to write our own, opposite function that returns only non-unique collection items
function nonUnique(array) {
var result = [];
var seen = [];
for (var i = 0, length = array.length; i < length; i++) {
var value = array[i];
if (seen.indexOf(value) === -1) { // warning! naive assumption
seen.push(value);
} else {
result.push(value);
}
}
console.log("non-unique result", result);
return result;
};
function hasMoreThanNUnique(array, threshold) {
var uArr = nonUnique(array);
var accum = 0;
for (var i = 0; i < array.length; i++) {
var val = array[i];
if (uArr.indexOf(val) === -1) {
accum++;
}
if (accum > threshold) return true;
}
return false;
}
var testArrA = [1, 1, 2, 2, 2, 3, 4, 5]; // unique values: [3, 4, 5]
var testArrB = [1, 1, 1, 1, 4]; // [4]
var testResultsA = hasMoreThanNUnique(testArrA, 3)
console.log("testArrA and results", testResultsA);
var testResultsB = hasMoreThanNUnique(testArrB, 3);
console.log("testArrB and results", testResultsB);
So far, I came up with the following:
const countNum = [1,1,1,2,1,1,3,1,1,1,4,1,1].reduce((r,n) => (r[n]=(r[n]||0)+1, r), {});
const res = Object.entries(countNum).some(([n,q]) => q == 1 ? !(m--) : false, m=2);
console.log(res);
.as-console-wrapper{min-height:100%}
But I don't really like array->object->array conversion about that. Is there a faster and (at the same time compact) solution?

How to find the number of subarrays in an array with given sum?

My program should be as following:
Input : {1,2,3,2,1,8,-3}, sum = 5
Output should be 3 example combinations ({2,3}, {3,2}, {8,-3}) have sum
exactly equal to 5.
I tried to do it in JavaScript but I'm confused.
function findSubarraySum(arr, sum) {
var res = 0;
var currentSum = 0;
for (var i = 0; i < arr.length; i++) {
currentSum += arr[i];
if (currentSum == sum)
res++;
}
return res;
}
console.log(findSubarraySum([1, 2, 3, 4], 10));
You first need a way to iterate over all the unique ways you can choose a start and and of your subarray boundaries (your slice definition).
In my code below, I use a combinations function to get all possible combinations of two indexes for the array supplied. You could do something else, like a simple doubly nested for loop.
Next you need to take the slice of the array according to the slice definition and reduce the elements into a sum. The Array.prototype.reduce function works well for that.
Finally, you want to include the subArray in the list of results only if the reduced sum matched the desired sum.
// Input : {1,2,3,2,1,8,-3}, sum = 5
const { combinations, range } = (() => {
const _combinations = function*(array, count, start, result) {
if (count <= 0) {
yield [...result]; // Yes, we want to return a copy
return;
}
const nextCount = count - 1;
const end = array.length - nextCount; // leave room on the array for the next remaining elements
for (let i = start; i < end; i += 1) {
// we have already used the element at (start - 1)
result[result.length - count] = array[i];
const nextStart = i + 1; // Always choose the next element from the ones following the last chosen element
yield* _combinations(array, nextCount, nextStart, result);
}
};
function* combinations(array, count) {
yield* _combinations(array, count, 0, Array(count));
}
function* range(l) {
for (let i = 0; i < l; i += 1) {
yield i;
}
}
return {
combinations,
range,
};
})();
const subArraysBy = (predicate, array) => {
const result = [];
for (const [beg, end] of combinations([...range(array.length+1)], 2)) {
const subArray = array.slice(beg, end);
if (predicate(subArray)) {
result.push(subArray);
}
}
return result;
};
const sum = array => array.reduce((sum, e) => sum + e);
console.log(
subArraysBy(
a => sum(a) === 5,
[1, 2, 3, 2, 1, 8, -3],
),
);
References:
MDN: Array.prototype.reduce
MDN: function* -- not required for your solution
Lodash: _.range -- implemented this in my code rather than use the lodash one. They work similarly.
Python Docs: combinations - My combinations implementation is inspired by python itertools.

An algorithm to find the closest values in javascript array

I'm working on a small algorithm to find the closest values of a given number in an random array of numbers. In my case I'm trying to detect connected machines identified by a 6-digit number ID ("123456", "0078965", ...) but it can be useful for example to find the closest geolocated users around me.
What I need is to list the 5 closest machines, no matter if their IDs are higher or lower. This code works perfectly but I'm looking for a smarter and better way to proceed, amha I got to much loops and arrays.
let n = 0; // counter
let m = 5; // number of final machines to find
// list of IDs founded (unordered: we can't decide)
const arr = ["087965","258369","885974","0078965","457896","998120","698745","399710","357984","698745","789456"]
let NUM = "176789" // the random NUM to test
const temp = [];
const diff = {};
let result = null;
// list the [m] highest founded (5 IDs)
for(i=0 ; i<arr.length; i++) {
if(arr[i] > NUM) {
for(j=0 ; j<m; j++) {
temp.push(arr[i+j]);
} break;
}
}
// list the [m] lowest founded (5 IDs)
for(i=arr.length ; i>=0; i--) {
if(arr[i] < NUM) {
for(j=m ; j>=0; j--) {
temp.push(arr[i-j]);
} break;
}
}
// now we are certain to get at least 5 IDs even if NUM is 999999 or 000000
temp.sort(function(a, b){return a - b}); // increase order
for(i=0 ; i<(m*2); i++) {
let difference = Math.abs(NUM - temp[i]);
diff[difference] = temp[i]; // [ 20519 : "964223" ]
}
// we now get a 10-values "temp" array ordered by difference
// list the [m] first IDs:
for(key in diff){
if(n < m){
let add = 6-diff[key].toString().length;
let zer = '0'.repeat(add);
let id = zer+diff[key]; // "5802" -> "005802"
result += (n+1)+":"+ id +" ";
n+=1;
}
}
alert(result);
-> "1:0078965 2:087965 3:258369 4:357984 5:399710" for "176789"
You actually don't need to have so many different iterations. All you need is to loop twice:
The first iteration attempt is to use .map() to create an array of objects that stores the ID and the absolute difference between the ID and num
The second iteration attempt is simply to use .sort() through the array of objects created in step 1, ranking them from lowest to highest difference
Once the second iteration is done, you simply use .slice(0, 5) to get the first 5 objects in the array, which now contains the smallest 5 diffs. Iterate through it again if you want to simply extract the ID:
const arr = ["087965","258369","885974","078965","457896","998120","698745","399710","357984","698745","789456"];
let num = "176789";
let m = 5; // number of final machines to find
// Create an array of objects storing the original arr + diff from `num`
const diff = arr.map(item => {
return { id: item, diff: Math.abs(+item - +num) };
});
// Sort by difference from `num` (lowest to highest)
diff.sort((a, b) => a.diff - b.diff);
// Get the first m entries
const filteredArr = diff.slice(0, m).map(item => item.id).sort();
// Log
console.log(filteredArr);
// Completely optional, if you want to format it the way you have in your question
console.log(`"${filteredArr.map((v, i) => i + ": " + v).join(', ')}" for "${num}"`);
You could take an array as result set, fill it with the first n elements and sort it by the delta of the wanted value.
For all other elements check if the absolute delta of the actual item and the value is smaller then the last value of the result set and replace this value with the actual item. Sort again. Repeat until all elements are processed.
The result set is ordered by the smallest delta to the greatest by using the target value.
const
absDelta = (a, b) => Math.abs(a - b),
sortDelta = v => (a, b) => absDelta(a, v) - absDelta(b, v),
array = [087965, 258369, 885974, 0078965, 457896, 998120, 698745, 399710, 357984, 698745, 789456],
value = 176789,
n = 5,
result = array.reduce((r, v) => {
if (r.length < n) {
r.push(v);
r.sort(sortDelta(value));
return r;
}
if (absDelta(v, value) < absDelta(r[n - 1], value)) {
r[n - 1] = v;
r.sort(sortDelta(value));
}
return r;
}, []);
console.log(result); // sorted by closest value
A few good approaches so far, but I can't resist throwing in another.
This tests a sliding window of n elements in a sorted version of the array, and returns the one whose midpoint is closest to the value you're looking for. This is a pretty efficient approach (one sort of the array, and then a single pass through that) -- though it does not catch cases where there's more than one correct answer (see the last test case below).
const closestN = function(n, target, arr) {
// make sure we're not comparing strings, then sort:
let sorted = arr.map(Number).sort((a, b) => a - b);
target = Number(target);
let bestDiff = Infinity; // actual diff can be assumed to be lower than this
let bestSlice = 0; // until proven otherwise
for (var i = 0; i <= sorted.length - n; i++) {
let median = medianOf(sorted[i], sorted[i+n-1]) // midpoint of the group
let diff = Math.abs(target - median); // distance to the target
if (diff < bestDiff) { // we improved on the previous attempt
bestDiff = diff; // capture this for later comparisons
bestSlice = i;
}
// TODO handle diff == bestDiff? i.e. more than one possible correct answer
}
return sorted.slice(bestSlice, bestSlice + n)
}
// I cheated a bit here; this won't work if a > b:
const medianOf = function(a, b) {
return (Math.abs(b-a) / 2) + a
}
console.log(closestN(5, 176789, ["087965", "258369", "885974", "0078965", "457896", "998120", "698745", "399710", "357984", "698745", "789456"]))
// more test cases
console.log(closestN(3, 5, [1,2,5,8,9])) // should be 2,5,8
console.log(closestN(3, 4, [1,2,5,8,9])) // should be 1,2,5
console.log(closestN(1, 4, [1,2,5,8,9])) // should be 5
console.log(closestN(3, 99, [1,2,5,8,9])) // should be 5,8,9
console.log(closestN(3, -99, [1,2,5,8,9])) // should be 1,2,5
console.log(closestN(3, -2, [-10, -5, 0, 4])) // should be -5, 0, 4
console.log(closestN(1, 2, [1,3])) // either 1 or 3 would be correct...

javascript permutation generator with permutation length parameter

I've seen a few generators out there but they all make a squared matrix. For example, you give it a list of three items and it'll assume the output of the length is also three. However, I'd like to specify the items and the length.
Sound like an easy problem can't believe there isn't a library available for it. Would like to avoid writing this myself if there's a tested library out there. Any suggestions would be great.
Example of what i've found
var list = 'abc';
perms = permutations(list);
//you cannot define the length
Example
var list = 'abc';
var length = 3;
perms = permutations(list,length);
console.log(perms);
/* output
a,a,a
a,b,c
a,b,a
a,c,a
c,a,a
...
*/
I would like to be able to change length and should create permutations accordingly
length = 2
a,a
a,b
b,b
b,a
length = 4
a,a,a,a
a,a,a,b
....
You can imagine the length as representing the number of slots. Each slot has N possibilities, given that N is the number of elements in your initial list. So given three values [1,2,3], you will have a total of 3 x 3 x 3 = 27 permutations.
Here's my attempt. Comments included!
var list = [1,2,3];
var getPermutations = function(list, maxLen) {
// Copy initial values as arrays
var perm = list.map(function(val) {
return [val];
});
// Our permutation generator
var generate = function(perm, maxLen, currLen) {
// Reached desired length
if (currLen === maxLen) {
return perm;
}
// For each existing permutation
for (var i = 0, len = perm.length; i < len; i++) {
var currPerm = perm.shift();
// Create new permutation
for (var k = 0; k < list.length; k++) {
perm.push(currPerm.concat(list[k]));
}
}
// Recurse
return generate(perm, maxLen, currLen + 1);
};
// Start with size 1 because of initial values
return generate(perm, maxLen, 1);
};
var res = getPermutations(list, 3);
console.log(res);
console.log(res.length); // 27
fiddle
If you're looking for an answer based on performance, you can use the length of the array as a numerical base, and access the elements in the array based on this base, essentially replacing actual values from the base with the values in your array, and accessing each of the values in order, using a counter:
const getCombos = (arr, len) => {
const base = arr.length
const counter = Array(len).fill(base === 1 ? arr[0] : 0)
if (base === 1) return [counter]
const combos = []
const increment = i => {
if (counter[i] === base - 1) {
counter[i] = 0
increment(i - 1)
} else {
counter[i]++
}
}
for (let i = base ** len; i--;) {
const combo = []
for (let j = 0; j < counter.length; j++) {
combo.push(arr[counter[j]])
}
combos.push(combo)
increment(counter.length - 1)
}
return combos
}
const combos = getCombos([1, 2, 3], 3)
console.log(combos)
For smaller use cases, like the example above, performance shouldn't be an issue, but if you were to increase the size of the given array from 3 to 10, and the length from 3 to 5, you have already moved from 27 (33) combinations to 100,000 (105), you can see the performance difference here:
I wrote a little library that uses generators to give you permutations with custom items and number of elements. https://github.com/acarl005/generatorics
const G = require('generatorics')
for (let perm of G.permutation(['a', 'b', 'c'], 2)) {
console.log(perm);
}
// [ 'a', 'b' ]
// [ 'a', 'c' ]
// [ 'b', 'a' ]
// [ 'b', 'c' ]
// [ 'c', 'a' ]
// [ 'c', 'b' ]

Categories

Resources