How to perform digit by digit multiplication in JS - javascript

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);

Related

JavaScript: Randomly select a limited number of objects from an array to be placed into a second array? [duplicate]

I am working on 'how to access elements randomly from an array in javascript'. I found many links regarding this. Like:
Get random item from JavaScript array
var item = items[Math.floor(Math.random()*items.length)];
But in this, we can choose only one item from the array. If we want more than one elements then how can we achieve this? How can we get more than one element from an array?
Just two lines :
// Shuffle array
const shuffled = array.sort(() => 0.5 - Math.random());
// Get sub-array of first n elements after shuffled
let selected = shuffled.slice(0, n);
DEMO:
Try this non-destructive (and fast) function:
function getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
There is a one-liner unique solution here
array.sort(() => Math.random() - Math.random()).slice(0, n)
lodash _.sample and _.sampleSize.
Gets one or n random elements at unique keys from collection up to the size of collection.
_.sample([1, 2, 3, 4]);
// => 2
_.sampleSize([1, 2, 3], 2);
// => [3, 1]
_.sampleSize([1, 2, 3], 3);
// => [2, 3, 1]
Getting 5 random items without changing the original array:
const n = 5;
const sample = items
.map(x => ({ x, r: Math.random() }))
.sort((a, b) => a.r - b.r)
.map(a => a.x)
.slice(0, n);
(Don't use this for big lists)
create a funcion which does that:
var getMeRandomElements = function(sourceArray, neededElements) {
var result = [];
for (var i = 0; i < neededElements; i++) {
result.push(sourceArray[Math.floor(Math.random()*sourceArray.length)]);
}
return result;
}
you should also check if the sourceArray has enough elements to be returned. and if you want unique elements returned, you should remove selected element from the sourceArray.
Porting .sample from the Python standard library:
function sample(population, k){
/*
Chooses k unique random elements from a population sequence or set.
Returns a new list containing elements from the population while
leaving the original population unchanged. The resulting list is
in selection order so that all sub-slices will also be valid random
samples. This allows raffle winners (the sample) to be partitioned
into grand prize and second place winners (the subslices).
Members of the population need not be hashable or unique. If the
population contains repeats, then each occurrence is a possible
selection in the sample.
To choose a sample in a range of integers, use range as an argument.
This is especially fast and space efficient for sampling from a
large population: sample(range(10000000), 60)
Sampling without replacement entails tracking either potential
selections (the pool) in a list or previous selections in a set.
When the number of selections is small compared to the
population, then tracking selections is efficient, requiring
only a small set and an occasional reselection. For
a larger number of selections, the pool tracking method is
preferred since the list takes less space than the
set and it doesn't suffer from frequent reselections.
*/
if(!Array.isArray(population))
throw new TypeError("Population must be an array.");
var n = population.length;
if(k < 0 || k > n)
throw new RangeError("Sample larger than population or is negative");
var result = new Array(k);
var setsize = 21; // size of a small set minus size of an empty list
if(k > 5)
setsize += Math.pow(4, Math.ceil(Math.log(k * 3) / Math.log(4)))
if(n <= setsize){
// An n-length list is smaller than a k-length set
var pool = population.slice();
for(var i = 0; i < k; i++){ // invariant: non-selected at [0,n-i)
var j = Math.random() * (n - i) | 0;
result[i] = pool[j];
pool[j] = pool[n - i - 1]; // move non-selected item into vacancy
}
}else{
var selected = new Set();
for(var i = 0; i < k; i++){
var j = Math.random() * n | 0;
while(selected.has(j)){
j = Math.random() * n | 0;
}
selected.add(j);
result[i] = population[j];
}
}
return result;
}
Implementation ported from Lib/random.py.
Notes:
setsize is set based on characteristics in Python for efficiency. Although it has not been adjusted for JavaScript, the algorithm will still function as expected.
Some other answers described in this page are not safe according to the ECMAScript specification due to the misuse of Array.prototype.sort. This algorithm however is guaranteed to terminate in finite time.
For older browsers that do not have Set implemented, the set can be replaced with an Array and .has(j) replaced with .indexOf(j) > -1.
Performance against the accepted answer:
https://jsperf.com/pick-random-elements-from-an-array
The performance difference is the greatest on Safari.
If you want to randomly get items from the array in a loop without repetitions you can remove the selected item from the array with splice:
var items = [1, 2, 3, 4, 5];
var newItems = [];
for (var i = 0; i < 3; i++) {
var idx = Math.floor(Math.random() * items.length);
newItems.push(items[idx]);
items.splice(idx, 1);
}
console.log(newItems);
ES6 syntax
const pickRandom = (arr,count) => {
let _arr = [...arr];
return[...Array(count)].map( ()=> _arr.splice(Math.floor(Math.random() * _arr.length), 1)[0] );
}
I can't believe that no one didn't mention this method, pretty clean and straightforward.
const getRnd = (a, n) => new Array(n).fill(null).map(() => a[Math.floor(Math.random() * a.length)]);
Array.prototype.getnkill = function() {
var a = Math.floor(Math.random()*this.length);
var dead = this[a];
this.splice(a,1);
return dead;
}
//.getnkill() removes element in the array
//so if you like you can keep a copy of the array first:
//var original= items.slice(0);
var item = items.getnkill();
var anotheritem = items.getnkill();
Here's a nicely typed version. It doesn't fail. Returns a shuffled array if sample size is larger than original array's length.
function sampleArr<T>(arr: T[], size: number): T[] {
const setOfIndexes = new Set<number>();
while (setOfIndexes.size < size && setOfIndexes.size < arr.length) {
setOfIndexes.add(randomIntFromInterval(0, arr.length - 1));
}
return Array.from(setOfIndexes.values()).map(i => arr[i]);
}
const randomIntFromInterval = (min: number, max: number): number =>
Math.floor(Math.random() * (max - min + 1) + min);
In this answer, I want to share with you the test that I have to know the best method that gives equal chances for all elements to have random subarray.
Method 01
array.sort(() => Math.random() - Math.random()).slice(0, n)
using this method, some elements have higher chances comparing with others.
calculateProbability = function(number=0 ,iterations=10000,arraySize=100) {
let occ = 0
for (let index = 0; index < iterations; index++) {
const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize]
/** Wrong Method */
const arr = myArray.sort(function() {
return val= .5 - Math.random();
});
if(arr[0]===number) {
occ ++
}
}
console.log("Probability of ",number, " = ",occ*100 /iterations,"%")
}
calculateProbability(0)
calculateProbability(0)
calculateProbability(0)
calculateProbability(50)
calculateProbability(50)
calculateProbability(50)
calculateProbability(25)
calculateProbability(25)
calculateProbability(25)
Method 2
Using this method, the elements have the same probability:
const arr = myArray
.map((a) => ({sort: Math.random(), value: a}))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value)
calculateProbability = function(number=0 ,iterations=10000,arraySize=100) {
let occ = 0
for (let index = 0; index < iterations; index++) {
const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize]
/** Correct Method */
const arr = myArray
.map((a) => ({sort: Math.random(), value: a}))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value)
if(arr[0]===number) {
occ ++
}
}
console.log("Probability of ",number, " = ",occ*100 /iterations,"%")
}
calculateProbability(0)
calculateProbability(0)
calculateProbability(0)
calculateProbability(50)
calculateProbability(50)
calculateProbability(50)
calculateProbability(25)
calculateProbability(25)
calculateProbability(25)
The correct answer is posted in in the following link: https://stackoverflow.com/a/46545530/3811640
2020
non destructive functional programing style, working in a immutable context.
const _randomslice = (ar, size) => {
let new_ar = [...ar];
new_ar.splice(Math.floor(Math.random()*ar.length),1);
return ar.length <= (size+1) ? new_ar : _randomslice(new_ar, size);
}
console.log(_randomslice([1,2,3,4,5],2));
EDIT: This solution is slower than others presented here (which splice the source array) if you want to get only a few elements. The speed of this solution depends only on the number of elements in the original array, while the speed of the splicing solution depends on the number of elements required in the output array.
If you want non-repeating random elements, you can shuffle your array then get only as many as you want:
function shuffle(array) {
var counter = array.length, temp, index;
// While there are elements in the array
while (counter--) {
// Pick a random index
index = (Math.random() * counter) | 0;
// And swap the last element with it
temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
}
var arr = [0,1,2,3,4,5,7,8,9];
var randoms = shuffle(arr.slice(0)); // array is cloned so it won't be destroyed
randoms.length = 4; // get 4 random elements
DEMO: http://jsbin.com/UHUHuqi/1/edit
Shuffle function taken from here: https://stackoverflow.com/a/6274398/1669279
I needed a function to solve this kind of issue so I'm sharing it here.
const getRandomItem = function(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
// original array
let arr = [4, 3, 1, 6, 9, 8, 5];
// number of random elements to get from arr
let n = 4;
let count = 0;
// new array to push random item in
let randomItems = []
do {
let item = getRandomItem(arr);
randomItems.push(item);
// update the original array and remove the recently pushed item
arr.splice(arr.indexOf(item), 1);
count++;
} while(count < n);
console.log(randomItems);
console.log(arr);
Note: if n = arr.length then basically you're shuffling the array arr and randomItems returns that shuffled array.
Demo
Here's an optimized version of the code ported from Python by #Derek, with the added destructive (in-place) option that makes it the fastest algorithm possible if you can go with it. Otherwise it either makes a full copy or, for a small number of items requested from a large array, switches to a selection-based algorithm.
// Chooses k unique random elements from pool.
function sample(pool, k, destructive) {
var n = pool.length;
if (k < 0 || k > n)
throw new RangeError("Sample larger than population or is negative");
if (destructive || n <= (k <= 5 ? 21 : 21 + Math.pow(4, Math.ceil(Math.log(k*3) / Math.log(4))))) {
if (!destructive)
pool = Array.prototype.slice.call(pool);
for (var i = 0; i < k; i++) { // invariant: non-selected at [i,n)
var j = i + Math.random() * (n - i) | 0;
var x = pool[i];
pool[i] = pool[j];
pool[j] = x;
}
pool.length = k; // truncate
return pool;
} else {
var selected = new Set();
while (selected.add(Math.random() * n | 0).size < k) {}
return Array.prototype.map.call(selected, i => pool[i]);
}
}
In comparison to Derek's implementation, the first algorithm is much faster in Firefox while being a bit slower in Chrome, although now it has the destructive option - the most performant one. The second algorithm is simply 5-15% faster. I try not to give any concrete numbers since they vary depending on k and n and probably won't mean anything in the future with the new browser versions.
The heuristic that makes the choice between algorithms originates from Python code. I've left it as is, although it sometimes selects the slower one. It should be optimized for JS, but it's a complex task since the performance of corner cases is browser- and their version-dependent. For example, when you try to select 20 out of 1000 or 1050, it will switch to the first or the second algorithm accordingly. In this case the first one runs 2x faster than the second one in Chrome 80 but 3x slower in Firefox 74.
Sampling with possible duplicates:
const sample_with_duplicates = Array(sample_size).fill().map(() => items[~~(Math.random() * items.length)])
Sampling without duplicates:
const sample_without_duplicates = [...Array(items.length).keys()].sort(() => 0.5 - Math.random()).slice(0, sample_size).map(index => items[index]);
Since without duplicates requires sorting the whole index array first, it is considerably slow than with possible duplicates for big items input arrays.
Obviously, the max size of without duplicates is <= items.length
Check this fiddle: https://jsfiddle.net/doleron/5zw2vequ/30/
It extracts random elements from srcArray one by one while it get's enough or there is no more elements in srcArray left for extracting.
Fast and reliable.
function getNRandomValuesFromArray(srcArr, n) {
// making copy to do not affect original srcArray
srcArr = srcArr.slice();
resultArr = [];
// while srcArray isn't empty AND we didn't enough random elements
while (srcArr.length && resultArr.length < n) {
// remove one element from random position and add this element to the result array
resultArr = resultArr.concat( // merge arrays
srcArr.splice( // extract one random element
Math.floor(Math.random() * srcArr.length),
1
)
);
}
return resultArr;
}
Here's a function I use that allows you to easily sample an array with or without replacement:
// Returns a random sample (either with or without replacement) from an array
const randomSample = (arr, k, withReplacement = false) => {
let sample;
if (withReplacement === true) { // sample with replacement
sample = Array.from({length: k}, () => arr[Math.floor(Math.random() * arr.length)]);
} else { // sample without replacement
if (k > arr.length) {
throw new RangeError('Sample size must be less than or equal to array length when sampling without replacement.')
}
sample = arr.map(a => [a, Math.random()]).sort((a, b) => {
return a[1] < b[1] ? -1 : 1;}).slice(0, k).map(a => a[0]);
};
return sample;
};
Using it is simple:
Without Replacement (default behavior)
randomSample([1, 2, 3], 2) may return [2, 1]
With Replacement
randomSample([1, 2, 3, 4, 5, 6], 4) may return [2, 3, 3, 2]
var getRandomElements = function(sourceArray, requiredLength) {
var result = [];
while(result.length<requiredLength){
random = Math.floor(Math.random()*sourceArray.length);
if(result.indexOf(sourceArray[random])==-1){
result.push(sourceArray[random]);
}
}
return result;
}
const items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'I', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 1, 2, 3, 4, 5];
const fetchRandomArray = ({pool=[], limit=1})=>{
let query = []
let selectedIndices = {}
while(query.length < limit){
const index = Math.floor(Math.random()*pool.length)
if(typeof(selectedIndices[index])==='undefined'){
query.push(items[index])
selectedIndices[index] = index
}
}
console.log(fetchRandomArray({pool:items, limit:10})
2019
This is same as Laurynas Mališauskas answer, just that the elements are unique (no duplicates).
var getMeRandomElements = function(sourceArray, neededElements) {
var result = [];
for (var i = 0; i < neededElements; i++) {
var index = Math.floor(Math.random() * sourceArray.length);
result.push(sourceArray[index]);
sourceArray.splice(index, 1);
}
return result;
}
Now to answer original question "How to get multiple random elements by jQuery", here you go:
var getMeRandomElements = function(sourceArray, neededElements) {
var result = [];
for (var i = 0; i < neededElements; i++) {
var index = Math.floor(Math.random() * sourceArray.length);
result.push(sourceArray[index]);
sourceArray.splice(index, 1);
}
return result;
}
var $set = $('.someClass');// <<<<< change this please
var allIndexes = [];
for(var i = 0; i < $set.length; ++i) {
allIndexes.push(i);
}
var totalRandom = 4;// <<<<< change this please
var randomIndexes = getMeRandomElements(allIndexes, totalRandom);
var $randomElements = null;
for(var i = 0; i < randomIndexes.length; ++i) {
var randomIndex = randomIndexes[i];
if($randomElements === null) {
$randomElements = $set.eq(randomIndex);
} else {
$randomElements.add($set.eq(randomIndex));
}
}
// $randomElements is ready
$randomElements.css('backgroundColor', 'red');
Here is the most correct answer and it will give you Random + Unique elements.
function randomize(array, n)
{
var final = [];
array = array.filter(function(elem, index, self) {
return index == self.indexOf(elem);
}).sort(function() { return 0.5 - Math.random() });
var len = array.length,
n = n > len ? len : n;
for(var i = 0; i < n; i ++)
{
final[i] = array[i];
}
return final;
}
// randomize([1,2,3,4,5,3,2], 4);
// Result: [1, 2, 3, 5] // Something like this
items.sort(() => (Math.random() > 0.5 ? 1 : -1)).slice(0, count);

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

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")

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

Find averages in an array of different numbers

I feel like I didn't phrase my title very well, can someone please correct it if you understand my question.
I have an array of
arr = [1,2,3,4,5,
6,7,8,9,0,
3,4,7,2,1,
4,6,1,2,3,
5,6,8,9,3
2,3,4,5,6
]
And I want to do several things
Split it into chunks with the size of 5
Calculate the number of chunks. In this case, it should be 6 chunks.
Calculate the sum of numbers of all chunks in each position and divide it by the total number of chunks. In this case,
(1+6+3+4+5+2)/6, (2+7+4+6+6+3)/6, ..., (5+0+1+3+3+6)/6
Return results as an array
var result = [3.5, 4.66, ..., 3]
I have got the idea, but not sure how to implement it.
Thanks
I believe this code accomplishes what you want.
function averageValues (arr) {
var chunks = Math.ceil(arr.length / 5); // find the number of chunks
var sums = [0, 0, 0, 0, 0]; // keep a running tally
for (var i = 0; i < arr.length; i ++) {
sums[i % 5] += arr[i]; // add each element to the proper part of the sum
}
for (var i = 0; i < sums.length; i ++) {
sums[i] /= chunks; // divide each part of the sum by the number of chunks
}
return sums;
}
You can solve this by maintaining five separate sums to end with five separate averages.
Prepare your sums array of length 5:
var sums = [ 0, 0, 0, 0, 0 ];
For each number in your set, increment the corresponding sum by that number.
for (var x = 0; x < arr.length; x++)
sums[x % 5] += arr[x];
Divide each sum by how many numbers were used:
var numbers = arr.length / 5; // 6 numbers each
var result = sums.map(
function(s) {
return s / numbers; // divide each sum by 6
}
);
This uses the assumption that your set length is always a multiple of 5.
Here is a more functional approach to your problem. This uses the assumption that your set length is always a multiple of 5.
// add extra array helpers
Array.prototype.eachSlice = function (n, fn) {
let slices = [];
for (let i = 0; i < this.length; i += n) {
let slice = this.slice(i, i + n);
slices.push(slice);
}
if (fn) slices.forEach(fn);
return slices;
}
Array.prototype.sum = function (fn) {
let fnReduce = fn ? (acc, ...args) => acc + fn(...args) : (acc, v) => acc + v;
return this.reduce(fnReduce, 0);
}
Array.prototype.avg = function (fn) {
return this.sum(fn) / this.length;
}
// actual solution
let arr = [
1,2,3,4,5,
6,7,8,9,0,
3,4,7,2,1,
4,6,1,2,3,
5,6,8,9,3,
2,3,4,5,6,
];
let chunkSize = 5;
console.log('--- question #1 ---');
console.log('Split it into chunks with the size of 5.');
console.log('-------------------');
let chunks = arr.eachSlice(chunkSize);
console.log(chunks);
console.log('--- question #2 ---');
console.log('Calculate the number of chunks. In this case, it should be 6 chunks.');
console.log('-------------------');
console.log(chunks.length);
console.log('--- question #3 ---');
console.log('Calculate the sum of numbers of all chunks in each position and divide it by the total number of chunks.');
console.log('-------------------');
let avgChunks = new Array(chunkSize).fill()
.map((_, i) => chunks.avg(chunk => chunk[i]));
console.log('See the result under question #4.');
console.log('--- question #4 ---');
console.log('Return results as an array.');
console.log('-------------------');
console.log(avgChunks);
It could be useful:
//The average method using an array
function average(arr) {
var sum = arr.reduce(function (a,b) { return a + b; },0)
return sum/ arr.length
}
//Chunk array method, it returns an array of the sliced arrays by size
function chunkArray(arr, chunkSize){
var numChunks = arr.length / chunkSize
var chunks= []
for (let index = 0; index < numChunks; index++) {
chunks.push(arr.slice(index * chunkSize, (index * chunkSize) + chunkSize))
}
return chunks
}
//Finally, the average of arrays, it returns the average of each array
function averageArrays(arrs){
return arrs.map(function (arr) {
return average(arr)
})
}
//Example of usage
var chunks = chunkArray([
1,2,3,4,5,
6,7,8,9,0,
3,4,7,2,1,
4,6,1,2,3,
5,6,8,9,3,
2,3,4,5,6
],5)
console.log(averageArrays(chunks))
I think #Aplet123 has the most straight forward and easy to understand approach, though I changed up a little bit to suit my needs.
var chunks = Math.ceil(arr.length / 5) // Find the number of chunks
var sums = new Array(5).fill(0) // Keeps a running tally and fill values 0
arr.map((x, i) => sums[i%5] += arr[i]) // add each element to the proper part of the sum
var avgs = sums.map((x) => x/chunks /divide each part of the sum by the number of chunks

Looping multiple if statements based on array length

Just started learning javascript.
Input could be something like.
1, 5, 2, 7
and my task is to figure out how many numbers is missing between the lowest number and highest number.
var sorted = statues.sort();
var ticker = 0;
var plusser = sorted[0] + 1;
var plusser1 = sorted[1] + 1;
var plusser2 = sorted[2] + 1;
var plusser3 = sorted[3] + 1;
if(sorted[1] != plusser) {
ticker++
}
if(sorted[2] != plusser1) {
ticker ++;
}
if(sorted[3] != plusser2) {
ticker ++;
}
if(sorted[4] != plusser3) {
ticker ++;
}
this works great if there only is 4 numbers of input however, that ain't always the case and i am sure this can be coded cleaner if you use some sort of loop. Can you guys help me?
Find the max and min number and loop through array and check if a number is not part of array.
var arr = [1, 5, 2, 7];
var numberMissing = 0;
for(var i = Math.min.apply(Math, arr) + 1 ; i < Math.max.apply(Math, arr); ++i){
if(arr.indexOf(i) === -1){
console.log(i);
++numberMissing;
}
}
console.log("Missing Number : " + numberMissing);
task is to figure out how many numbers is missing between the lowest number and highest number
Sort the numbers : This will give the smallest number and largest number.
Subtract largest number and smallest number : This will give total numbers that could be included that range. Lets say this is N
Subtract Array Length with N : This will give number of missing number in the given array.
Since the question is to count and not to list all the missing numbers, we can take this approach. Following is the code example.
var input = [1,5,2,7];
var sortedInput = input.sort(); // this will work only for single digit array.
var firstNum = sortedInput[0],
lastNum = sortedInput[sortedInput.length-1],
numbersInRange = lastNum - firstNum +2; // +2 to include the numbers that are the range
var missingNumbers = numbersInRange - input.length;
console.log(missingNumbers)
If the array contains unique numbers (ie - 5 can't appear twice), you can use simple math:
var statues = [1, 5, 2, 7];
var result =
Math.max.apply(Math, statues) - Math.min.apply(Math, statues) + 1 // the amount of items that should be in the array
-
statues.length; // the current amount of items
console.log(result);
If you want the numbers as well, create a map of the existing numbers, and then create an array, that contain all numbers that don't exist in the initial array:
var statues = [1, 5, 2, 7];
function getMissingNumbers(arr) {
var result = [];
var map = arr.reduce(function(map, n) { // create a map of existing numbers
map[n] = true;
return map
}, {});
var max = Math.max.apply(Math, arr); // find the max
var min = Math.min.apply(Math, arr); // find the min
for(var i = min; i < max; i++) { // run from min to max
map[i] || result.push(i); // add only numbers that don't exist in the map
}
return result;
}
var result = getMissingNumbers(statues);
console.log('Missing items: ', result);
console.log('Number of missing items: ', result.length);
Here is a simple solution you can try :
var a = [1,5,2,7];
a.sort((a, b) => a-b)
.reduce((acc, element, index) => {
if(index) acc = acc + element - a[index-1] - 1; return acc;
}, 0);

Categories

Resources