Calculating Josephus Permutations efficiently in Javascript - javascript

While training in code-wars I came across a challenge about Joseph-permutations, I tried solving it on paper first and latter translate it to code.
The problem is as follows:
"Create a function that returns a Josephus permutation, taking as parameters the initial array/list of items to be permuted as if they were in a circle and counted out every k places until none remained."
My main idea was:
Have an auxiliary array to keep the response
Use two iterators:
i: to keep track of current index in the given array
k: to keep track of the step of the permutation
Initialize i at 0 and k at 1
When the original array has only one element left:
Push element to output array
Whenever i is not the last index of the array:
If k = step:
Take the element out of the original array, push it into the output array and finally replace k = 1
If k != step:
Increment i and k
When i is the last index of the original array (and the array has more than one element):
If k = step:
Take the element out of the original array, push it into the output array, replace k = 1 and set i = 0
If k != step:
Set i = 0 and increment k
function josephus(items,step){
var output = [];
var i = 0;
var k = 1;
if( items == [] ) {
return [];
}
while (items.length != 1) {
if (k == step && i == items.length - 1) {
output.push(items[i]);
items.splice(i, 1);
i = 0;
k = 1;
} else if (k == step && i != items.length - 1) {
output.push(items[i]);
items.splice(i, 1);
k = 1
} else if (k < step && i == items.length - 1) {
k++;
i=0;
} else if (k < step && i != items.length - 1) {
k++;
i++;
}
}
output.push(items[0]);
return output;
}
This works but it's not efficient, when I run it on the run sample tests I get that 5 of the sample tests have passed but it also includes an STDERR: Execution Timed Out (12000 ms).
The sample tests are the following ones:
Test.assertSimilar(josephus([1,2,3,4,5,6,7,8,9,10],1),[1,2,3,4,5,6,7,8,9,10])
Test.assertSimilar(josephus([1,2,3,4,5,6,7,8,9,10],2),[2, 4, 6, 8, 10, 3, 7, 1, 9, 5])
Test.assertSimilar(josephus(["C","o","d","e","W","a","r","s"],4),['e', 's', 'W', 'o', 'C', 'd', 'r', 'a'])
Test.assertSimilar(josephus([1,2,3,4,5,6,7],3),[3, 6, 2, 7, 5, 1, 4])
Test.assertSimilar(josephus([],3),[])
My question is, how could I make this more efficient?
Is it the algorithm that I'm using that's wrong or is it the implementation?
A comment mentioned two things:
push() is very slow, which was one of my possibilities (wrong data structure)
suggested looking at recursion (which goes more into my doubt about the algorithm). I'm not really seeing how to make this recursive though.
Thanks in advance for your help!

There's a recurrence, which could be memoized. (This seems to pass the Codewars tests.)
function g(n, k, i, memo){
if (memo.hasOwnProperty([n, k, i]))
return memo[[n, k, i]];
if (i == 1)
return memo[[n, k, i]] = (k - 1) % n;
return memo[[n, k, i]] =
(k + g(n - 1, k, i - 1, memo)) % n;
}
function f(A, k){
let n = A.length;
let result = new Array(n);
let memo = {};
for (let i=1; i<=n; i++)
result[i - 1] = A[ g(n, k, i, memo) ];
return result;
}
let str = '';
str += JSON.stringify(f([1,2,3,4,5,6,7,8,9,10],1)) + '\n';
//[1,2,3,4,5,6,7,8,9,10])
str += JSON.stringify(f([1,2,3,4,5,6,7,8,9,10],2)) + '\n';
//[2, 4, 6, 8, 10, 3, 7, 1, 9, 5])
str += JSON.stringify(f(["C","o","d","e","W","a","r","s"],4)) + '\n';
//,['e', 's', 'W', 'o', 'C', 'd', 'r', 'a'])
str += JSON.stringify(f([1,2,3,4,5,6,7],3)) + '\n';
//,[3, 6, 2, 7, 5, 1, 4])
str += JSON.stringify(f([],3))
//,[])
console.log(str);
To explain the recurrence, the first element removed (when i = 1) is clearly (k - 1) mod n (zero-indexed). Now consider finding g(n, k, i). The first element that gets removed is (k - 1) mod n and then we start at the kth position. So the problem is then to find the (i - 1)th element removed after removing the element at (k - 1) mod n and starting at k, which is (k + g(n - 1, k, i - 1)) mod n.

have you tried implementing the functional approach?
from wikipedia:
function getSafePosition(n) {
// find value of L for the equation
valueOfL = n - highestOneBit(n);
safePosition = 2 * valueOfL + 1;
return safePosition;
}
function highestOneBit(i) {
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
return i - (i >> 1);
}
this should run in O(n)

You could shift the leading bit to the end.
const josephus = (x) => parseInt(x.toString(2).substr(1) + 1, 2);

Related

How do I find the maximum path sum with path indication?

I have to find the maximum path sum starting from top right to bottom left of the matrix.
Therefore this is what I was asked to do:
Given an mxn grid filled with numbers, find a path from top left to
bottom right, which maximizes the sum of all numbers along its path,
also i can only move either down or right.
So I had this on a coding interview recently and I passed it, but in the middle of the interview the guy asked me to also show the path along with the result
Example:
grid = [[1,10]
,[2,3]]
output: 14, right,down
So I came up with a solution:
function matrix(grid) {
var m = grid.length;
var n = grid[0].length;
if(!m || !n) return grid;
for (let i = 0; i < m; i++){
for (let j = 0; j < n; j++){
// grid[0][0] itself contains the min path sum
if (i === 0 && j === 0) continue;
// first row: grid[i][j] = previous grid(left) + current value
if (i === 0) grid[i][j] += grid[i][j-1];
// first column: grid[i][j] = previous grid(top) + current value
else if (j === 0) grid[i][j] += grid[i-1][j];
// grid[i][j] = get the min of previous grid(top/left) + current value
else grid[i][j] += Math.max(grid[i][j - 1], grid[i - 1][j]);
}
};
return grid[n - 1][m - 1];
};
const grid = [[1,10],[2,3]];
console.log(matrix(grid));
What I tried during the interview was to add the given grid and the result of the maximum numbers on each step on a variable each and then try to subtract the numbers that was calculated in the la line on my 2nd for loop compare it with the number in the original grid and if it match then push 'down' or 'right' to an empty array and then simply return the result and the array.
Example:
grid = [[1,10]
,[2,3]]
oldGrid = grid
arr == []
loop
on each iteration
grid[1] - oldGrid[1] = oldgrid[0]
{for rows}
add "right" to arr
or
grid[2] - oldgrid[2] = oldgrid[0]
{for cols}
add "down" to arr
return result and arr
so this is what i've come up with
function matrix(grid) {
var m = grid.length;
var n = grid[0].length;
var showDir = [];
if(!m || !n) return grid;
for (let i = 0; i < m; i++){
for (let j = 0; j < n; j++){
// grid[0][0] itself contains the min path sum
if (i === 0 && j === 0) continue;
// first row: grid[i][j] = previous grid(left) + current value
if (i === 0) grid[i][j] += grid[i][j-1];
// first column: grid[i][j] = previous grid(top) + current value
else if (j === 0) grid[i][j] += grid[i-1][j];
// grid[i][j] = get the min of previous grid(top/left) + current value
else{
let direction, max;
if(grid[i][j-1]>grid[i-1][j]){
direction = "R"
max = grid[i][j-1]
showDir.push(direction)
}
else{
direction = "D"
max = grid[i-1][j]
showDir.push(direction)
}
grid[i][j] += max;
}
};
};
return grid[n - 1][m - 1], showDir
};
const grid = [[1,10],[2,3]];
console.log(matrix(grid));
the thing is with this it only output showDir not the grid and i don't
know why
I'm new to Javascript so please go easy on me
You could iterate the diagonals and take the maximum value of either the upper item or the right one.
The final result is in bottom left item.
To get a path, you start from the end and choose the item with the next larger sum and take this position for the result set and get the next larger item's position until you reach the start item.
const
exist = (i, j) => i >= 0 && i < size[0] && j >= 0 && j < size[1],
array = [[1, 3, 4, 2, 5], [2, 4, 1, 7, 6], [9, 4, 1, 2, 1], [3, 5, 1, 1, 2], [3, 3, 7, 1, 4]],
size = [array.length, array[0].length],
sums = [],
path = [];
// get sums
for (let k = 0, l = size[0] + size[1] - 1; k < l; k++) {
let i = Math.min(k, size[0] - 1),
j = k - i;
while (exist(i, j)) {
sums[i] ??= [];
sums[i][j] = array[i][j] + Math.max(sums[i - 1]?.[j] || 0, sums[i][j - 1] || 0);
i--; j++;
}
}
// get path from max to start value
let i = size[0] - 1,
j = size[1] - 1;
path.push([i, j]);
while (i || j) {
if (!exist(i - 1, j) || exist(i, j - 1) && sums[i - 1][j] < sums[i][j - 1]) j--;
else i--;
path.unshift([i, j]);
}
path.forEach(([i, j]) => console.log(i, j, '->', sums[i][j]));
console.log('');
sums.forEach(a => console.log(...a.map(v => v.toString().padStart(3, ' '))));
.as-console-wrapper { max-height: 100% !important; top: 0; }
My approach is to build up a new grid with values such as {value: 56, path:["right", "down", "right", "down"]}, by iterating over the rows and columns and calculating each according to the one above it and the one to the left of it, if they exist, turning this:
[
[1, 10, 7],
[2, 30, 9],
[25, 2, 6]
]
into something like this:
[
[{value: 1, path: []}, {value: 11, path: [➡]}, {value: 18, path: [➡➡]}]
[{value: 3, path: [⬇]}, {value: 41, path: [➡⬇]}, {value: 50, path: [➡⬇➡]}]
[{value: 28, path: [⬇⬇]}, {value: 43, path: [➡⬇⬇]}, {value: 56, path: [➡⬇➡⬇]}]
]
(with the strings "right" and "down" replaced by arrows for simpler display) and then simply reading off the bottom-right value.
Here is some code doing that with a double-reduce to iterate the rows and then the cells within those rows:
const maxPath = (grid) => grid .reduce ((rs, r, j) => [...rs, r .reduce ((cs, c, i, _,
above = j > 0 && rs [j - 1] [i],
left = i > 0 && cs [i - 1]
) => [
... cs,
above && (!left || above .value > left .value)
? {value: above .value + c, path: [... above .path, 'down']}
: left && (!above || left .value >= above .value)
? {value: left .value + c, path: [... left .path, 'right']}
: {value: c, path: []}
], [])], []) [grid .length - 1] [grid [0] .length - 1]
console .log (maxPath ([
[1, 10, 7],
[2, 30, 9],
[25, 2, 6]
]))
The conditions in that version are a little complex. I started with this version:
const maxPath = (grid) => grid .reduce ((rs, r, j) => [...rs, r .reduce ((cs, c, i, _,
above = j > 0 && rs [j - 1] [i],
left = i > 0 && cs [i - 1]
) => [
... cs,
above && left
? above .value > left .value
? {value: above .value + c, path: [... above .path, 'down']}
: {value: left .value + c, path: [... left .path, 'right']}
: above
? {value: above .value + c, path: [... above .path, 'down']}
: left
? {value: left .value + c, path: [... left .path, 'right']}
: {value: c, path: []}
], [])], []) [grid .length - 1] [grid [0] .length - 1]
and then combined those conditions which led to the same result.

How to Remove any consecutive sequence in Javascript?

I am trying to solve this.
Nominal Case:
For the array[1,2,3,5,2,4,7,54], and the number 6. The sequences[1,2,3] and [4,2] will be removed because the add up to 6. function will return [5,7,54]. If two sequences overlap, remove the first sequence.
Overlapping Case:
For the array [1,2,3,9,4,1,4,6,7] and the number 5, the sequence [2,3,] and [4,1] are removed because they add up to 5. For the [4,1] case. you see that [4,1,4] represents two overlapping sequences. because [4,1] adds up to 5 first is removed and the 4 is not removed even through [1,4] adds up to 5. We say that [4,1] and [1,4] overlap to give [4,1,4] and in those cases the first of the overlapping sequences is removed . functin will return [1,9,4,6,7]
function consecutive(arr, len, num) {
var newarr = [];
for (let i = 1; i < len; i++) {
var sum = arr[i] + arr[i + 1];
if (sum == num) {
newarr.push(arr[i]);
newarr.push(arr[i + 1]);
}
}
return newarr;
}
let arr = [1, 2, 3, 5, 2, 4, 7, 54];
let len = arr.length;
let num = 6;
console.log(consecutive(arr, len, num));
Get Wrong Output
[2,4]
You could store the target index of wrong items and if no one to filter out check the next elements if they sum up to the wanted value.
function consecutive(array, num) {
return array.filter(
(wrong => (v, i, a) => {
if (i <= wrong) return false;
let sum = 0, j = i;
while (j < a.length) {
if ((sum += a[j]) === num) {
wrong = j;
return false;
}
j++;
}
return true;
})
(-1)
);
}
console.log(consecutive([1, 2, 3, 5, 2, 4, 7, 54], 6));

Flexible algorithm to calculate possibilities of all possible scenarios

I've been struggling for a little while to find or figure out algorithm.
The task:
Basically, I have an array of probabilities:
var input = [0.1, 0.2, 0.3, 0.1];
Let's name these inputs accordingly to: A, B, C and D.
And I also have a variable "m", which can tell me how many of these things needs to happen in order to get the result.
For example:
var m = 2;
This variable m is telling me that the event will happen if any of those two (or more) probabilities will happen.
So in this case, for event to happen, all possible ways for event to happen is:
ABCD
ABC
ABD
BCD
AB
AC
AD
BC
BD and CD
Now I need to calculate their probabilities, which I already have algorithms to calculate AND and OR (where input is just a probabilities array).
AND:
if (input.length > 0) {
output = 1;
}
for (i = 0; i < input.length; i++) {
output = input[i] * output;
}
OR:
if (input.length > 0) {
output = input[0];
}
for (i = 1; i < input.length; i++) {
output = (output + input[i]) - (output * input[i]);
}
So I am struggling on figuring out how to loop through all possible possibilities... And to have something like:
(A and B and C and D) or (A and B and C) or (A and B and D)... and so on... I hope you get the idea.
Here's a simple non-recursive solution to enumerate all combinations with at least m elements.
range = n => [...Array.from({length: n}).keys()]
mask = xs => b => xs.filter((_, n) => b & (1 << n))
at_least = n => xs => xs.length >= n
//
a = [...'ABCD']
m = 2
result = range(1 << a.length).map(mask(a)).filter(at_least(m))
console.log(result.map(x => x.join('')))
Since JS bit arithmetics is limited to 32 bits, this only works for m < 32.
You could get the combinations of your desired array with a minimum of two by using a recursive function which generates all the possible combinations.
function getC(array, min) {
function iter(i, temp) {
var t = temp.concat(array[i]);
if (i === array.length) return;
iter(i + 1, t);
iter(i + 1, temp);
if (t.length >= min) {
result.push(t);
}
}
var result = [];
iter(0, []);
return result;
}
var input = [0.1, 0.2, 0.3, 0.1];
console.log(getC(input, 2).map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could go over all combimations of 2 elements (AB, CD etc.) with two nested loops:
for(let i = 0; i < input.length; i++) {
for(let j = i + 1; j < input.length; j++) {
// possible combination: i and j
for(let k = j; k < input.length; k++) {
// possible combination: i, j, k
// and so on
}
}
}
for at least m elements that can be generalized with a nested generator that generates an array of indices ([0, 1], [0, 2], [1, 2], [0, 1, 2]):
function* combinations(length, m = 1, start = 0) {
// Base Case: If there is only one index left, yield that:
if(start === length - 1) {
yield [length - 1];
return;
}
// Otherwise go over all left indices
for(let i = start; i < length; i++) {
// And get all further combinations, for 0 that will be [1, 2], [1] and [2]
for(const nested of combinations(length, m - 1, i + 1)) {
// Yield the nested path, e.g. [0, 1], [0, 1, 2] and [0, 2]
yield [i, ...nested];
}
// If the minimum length is already reached yield the index itself
if(m <= 1) yield [i];
}
}
Now for every combination, we just have to multiply the probabilities and add them up:
let result = 0;
for(const combination of combimations(input.length, m))
result += combination.reduce((prev, i) => prev * input[i], 1);

Find the smallest sum of all indexes of unique number pairs summing to a given number

I want to loop through an array and then add each value to each other (except itself + itself) and if the sum of the two values that were looped through equals the second argument in my function, and the pair of values hasn't been encountered before, then remember their indices and, at the end, return the full sum of all remembered indices.
In other words, the problem statement is: given an array A of integers and a second value s that is a desired sum, find all pairs of values from array A at indexes i, j such that i < j and A[i] + A[j] = s, and return the sum of all indexes of these pairs, with the following restriction:
don't reuse value pairs, i.e. if two index pairs i, j and k, l satisfying the above conditions are found and if A[i] == A[k] and A[j] == A[l] or A[i] == A[l] and A[j] == A[k], then ignore the pair with the higher index sum.
Example
For example, functionName([1, 4, 2, 3, 0, 5], 7) should return 11 because values 4, 2, 3 and 5 can be paired with each other to equal 7 and the 11 comes from adding the indices of them to get to 11 where:
4 + 3 = 7
5 + 2 = 7
4 [index: 1]
2 [index: 2]
3 [index: 3]
5 [index: 5]
1 + 2 + 3 + 5 = 11
Example #2
functionName([1, 3, 2, 4], 4) would only equal 1, because only the first two elements can be paired to equal 4, and the first element has an index of 0 and the second 1
1 + 3 = 4
1 [index: 0]
3 [index: 1]
0 + 1 = 1
This is what I have so far:
function functionName(arr, arg) {
var newArr = [];
for(var i = 0; i < arr.length; i++){
for(var j = i + 1; j < arr.length; j++) {
if((arr[i] + arr[j]) === arg ) {
newArr.push(i , j);
}
}
}
if(newArr.length === 0) {
return console.log(0);
}
return console.log(newArr.reduce(function(a,b){return a + b}));
}
functionName([1, 4, 2, 3, 0, 5], 7);
The problem I have is that it all works but I have the issue that once it finds a pair that equals the second argument, then it's not supposed to use the same value pairs again but mine does, e.g.:
if the array is [1,1,1] and the second argument is 2, the loop will go through and find the answer but it continues to search after it finds the sum and I only want it to use the pair [1, 1] once, so if it finds a pair like this at indexes [0, 1] then it should not include any other pair that contains the value 1.
I was thinking that i could remove the rest of the values that are the same if more than 2 are found using filter leaving me with only 2 of the same value if there is in an array thus not having to worry about the loop finding a 1 + 1 twice but is this the best way to go about doing it?
I'm still new to this but looking forward to your comments
PS I'm planning on doing this using pure JavaScript and no libraries
Link to a JS fiddle that might make things easier to see what I have.
https://jsfiddle.net/ToreanJoel/xmumv3qt/
This is more complicated than it initially looks. In fact, making a loop inside a loop causes the algorithm to have quadratic time complexity with regard to the size of the array. In other words, for large arrays of numbers, it will take a very long time to complete.
Another way to handle this problem is to notice that you actually have to use each unique value in the array only once (or twice, if s is even and you have two s/2 values somewhere in the array). Otherwise, you would have non-unique pairs. This works because if you need pairs of numbers x and y such that x + y = s, if you know x, then y is determined -- it must be equal s - x.
So you can actually solve the problem in linear time complexity (to be fair, it's sometimes n*log(n) if all values in A are unique, because we have to sort them once).
The steps of the algorithm are as follows:
Make a map whose keys are values in array A, and values are sorted lists of indexes these values appear at in A.
Move through all unique values in A (you collected them when you solved step 1) in ascending order. For each such value:
Assume it's the lower value of the searched pair of values.
Calculate the higher value (it's equal to s - lower)
Check if the higher value also existed in A (you're doing it in constant time thanks to the map created in step 1).
If it does, add the lowest indexes of both the lower and the higher value to the result.
Return the result.
Here's the full code:
function findSumOfUniquePairs(numbers, sum) {
// First, make a map from values to lists of indexes with this value:
var indexesByValue = {},
values = [];
numbers.forEach(function (value, index) {
var indexes = indexesByValue[value];
if (!indexes) {
indexes = indexesByValue[value] = [];
values.push(value);
}
indexes.push(index);
});
values.sort();
var result = 0;
for (var i = 0, maxI = values.length; i < maxI; ++i) {
var lowerValue = values[i],
higherValue = sum - lowerValue;
if (lowerValue > higherValue) {
// We don't have to check symmetrical situations, so let's quit early:
break;
}
var lowerValueIndexes = indexesByValue[lowerValue];
if (lowerValue === higherValue) {
if (lowerValueIndexes.length >= 2) {
result += lowerValueIndexes[0] + lowerValueIndexes[1];
}
} else {
var higherValueIndexes = indexesByValue[higherValue];
if (higherValueIndexes) {
result += lowerValueIndexes[0] + higherValueIndexes[0];
}
}
}
return result;
}
document.write(findSumOfUniquePairs([1, 4, 2, 3, 0, 5], 7) + '<br>'); // 11;
document.write(findSumOfUniquePairs([1, 3, 2, 4], 4) + '<br>'); // 1
document.write(findSumOfUniquePairs([1, 1, 1], 2) + '<br>'); // 1
document.write(findSumOfUniquePairs([1, 1, 1, 1], 2) + '<br>'); // 1
document.write(findSumOfUniquePairs([1, 2, 3, 1, 2, 3, 1], 4) + '<br>'); // 7
document.write(findSumOfUniquePairs([5, 5, 1, 1, 1], 6) + '<br>'); // 2
document.write(findSumOfUniquePairs([0, 5, 0, 5, 1, 1, 1], 6) + '<br>'); // 5
This works, but it mucks up the initial array.
function functionName(arr, arg) {
var newArr = [];
for(var i = 0; i < arr.length; i++){
for(var j = i + 1; j < arr.length; j++) {
if((arr[i] + arr[j]) === arg ) {
newArr.push(i , j);
arr[i] = null;
arr[j] = null;
}
}
}
if(newArr.length === 0) {
return console.log(0);
}
return console.log(newArr.reduce(function(a,b){return a + b}));
}
Solution with loops with restart, if a sum is found. the found summands are stored in usedNumbers and later sorted and used to get the index for summing the index.
The sorting and the last index provides the correct start position for the Array.prototype.indexOf.
Edit:
what about [1,1,1,1], 2 ... should that be 6 or 1? – Jaromanda X 21
#JaromandaX that should be 1, after the pair is found with the values then it shouldn't look for a pair with the same values again – Torean
This version takes care of the requirement.
function f(array, sum) {
var arrayCopy = array.slice(0),
usedNumbers = [],
index = 0,
indexA = 0,
indexB,
a, b;
while (indexA < arrayCopy.length) {
indexB = indexA + 1;
while (indexB < arrayCopy.length) {
a = arrayCopy[indexA];
b = arrayCopy[indexB];
if (a + b === sum) {
usedNumbers.push(a, b);
arrayCopy = arrayCopy.filter(function (i) { return a !== i && b !== i; });
indexA--; // correction to keep the index
break;
}
indexB++;
}
indexA++;
}
return usedNumbers.sort().reduce(function (r, a, i) {
index = array.indexOf(a, i === 0 || a !== usedNumbers[i - 1] ? 0 : index + 1);
return r + index;
}, 0);
}
document.write(f([1, 4, 2, 3, 0, 5], 7) + '<br>'); // 11
document.write(f([1, 1, 1], 2) + '<br>'); // 1
document.write(f([5, 5, 1, 1, 1], 6) + '<br>'); // 2
document.write(f([0, 5, 0, 5, 1, 1, 1], 6) + '<br>'); // 5
document.write(f([1, 1, 1, 1], 2) + '<br>'); // 1
The solution below is very compact. It avoids unnecessary checks and loops only through the relevant elements. You can check the working codepen here:
http://codepen.io/PiotrBerebecki/pen/RRGaBZ.
function pairwise(arr, arg) {
var sum = 0;
for (var i=0; i<arr.length-1; i++) {
for (var j=i+1; j<arr.length; j++) {
if (arr[i] <= arg && arr[j] <= arg && arr[i] + arr[j] == arg) {
sum += i+j;
arr[i] = arr[j] = NaN;
}
}
}
return sum;
}
console.log( pairwise([1, 1, 0, 2], 2) ) // should return 6
Under the hood:
Start looping from the element with index (i) = 0.
Add a second loop only for the elements which are later in the array. Their index j is always higher than i as we are adding 1 to i.
If both elements (numbers) are less than or equal to to the arg, check if their sum equals to the arg. This avoids checking the sum if either of the numbers are greater than the arg.
If the pair has been found then change their values to NaN to avoid further checks and duplication.
This solution should have a time complexity of 0(n) or linear
Much faster than two nested for-loops. This function will give you the two indices that add up to the target number. It can easily be modified to solve any other configuration of this problem.
var twoSum = function(nums, target) {
const hash = {}
for(let i = 0; i < nums.length; i++) {
hash[nums[i]] = i
}
for(let j = 0; j < nums.length; j++) {
let numToFind = target - nums[j]
if(numToFind in hash && hash[numToFind] !== j) {
return [hash[numToFind], j]
}
}
return false
};
console.log(twoSum([1,2,3,5,7], 5))
In Python:
def twoSum(self, nums: List[int], target: int) -> List[int]:
myMap = {}
for i in range(len(nums)):
myMap[nums[i]] = i
for j in range(len(nums)):
numToFind = target - nums[j]
if numToFind in myMap and myMap[numToFind] != j:
return [myMap[numToFind], j]
print(twoSum([1,2,3,5,7], 5))
In Java:
import java.util.*;
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for(Integer i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for(Integer j = 0; j < nums.length; j++) {
Integer numToFind = target - nums[j];
Integer myInt = map.get(numToFind);
if(map.containsKey(numToFind) && myInt != j) {
return new int[] {myInt , j};
}
}
return new int[] {0, 0};
}
}
System.out.println(twoSum([1,2,3,5,7], 5))

Processing an array by sub arrays

Suppose we have an array of variable length, and I want to process it by chunks that are of a maximum length of 100, and do it in the minimum number of chunks. So for an array of length 241, it would be 3 sub arrays of sizes 41, 100, 100 (or 100, 100, 41).
curr_len = arr.length;
offset = curr_len%100;
doSomethingWithSubArray(arr.slice(offset))
for(j = offset; j <= curr_len; j = j+100){
doSomethingWithSubArray(arr.slice(j,j+100))
}
I'm sure there are more elegant ways of doing this, possibly without the special case before the for loop. Any ideas?
I'd expect the last chunk to be of smaller size. The code then would be:
for (var i=0; i<arr.length; i+=100)
doSomethingWithSubArray(arr.slice(i, 100));
This is exactly what my splitBy function does:
Array.prototype.splitBy = function(n) {
/* get: number of items per array
return: array of n-sized arrays with the items (last array may contain less then n) */
for (var r=[], i=0; i<this.length; i+=n)
r.push(this.slice(i, i+n));
return r;
}
Then write only:
arr.splitBy(100).forEach(doSomethingWithSubArray);
use chunk function~
function chunk(a, s){
for(var x, i = 0, c = -1, l = a.length, n = []; i < l; i++)
(x = i % s) ? n[c][x] = a[i] : n[++c] = [a[i]];
return n;
}
console.log(chunk([1,2,3,4,5,6,7,8,9,10], 3));
it's functional style recursive solutions.
no var, no loop, no count, because it's more cleary
var chunk = function(arr, n){
if (arr.length == 0) return [];
var head = arr.slice(0, n), rest = arr.slice(n);
return [head].concat( chunk(rest, n) );
};
console.log(chunk([1,2,3,4,5,6,7,8,9,10], 3));​
Not really, using reduce looks like this:
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
var splitArrays = array.reduce(function(arr, cur, i) {
if (i % 3 === 0) arr.push([]);
arr[i / 3 | 0].push(cur);
return arr;
}, []);
//splitArrays looks like:
//[[1,2,3],[4,5,6],[7,8,9],[10,11]]
More generic function
function splitArray(array, num) {
return array.reduce(function(arr, cur, i) {
if (i % num === 0) arr.push([]);
arr[i / num | 0].push(cur);
return arr;
}, []);
}
Make your doSomethingWithSubArray function accept a starting index and return a next unprocessed index or null if there's no more work. Put this "iterator" in a while loop. Do rest of work that you want to do between chunks (update UI?) right after calling this "iterator" in a while condition.

Categories