Divide array of nums into 2 balanced arrays - javascript

I'm take a 2d array that looks something like [[player1, 10], [player2, 8]] but with around 12 players. I'm satisfied enough with the sorting I have hear, except with this method teamA always gets the "first pick" of the better player. I am struggling to figure out a way to give teamB the better player every other time. below is the code as is that works "well enough".
data = [["player1", 10]. ["player2", 8], ["player3", 7], ["player4", 9]];
var teamA = [];
var teamB = [];
var remaining = [];
for (item in data) {
remaining.push(data[item].slice());
}
for (i in data) {
var max = 0;
var selection = [,];
var index = -1;
for (k in remaining) {
if (remaining[k][1] > max) {
selection = remaining[k];
max = remaining[k][1];
index = k;
}
}
remaining.splice(index, 1);
if (i % 2 == 0) {
teamA.push(selection);
} else {
teamB.push(selection);
}
}
This results in teamA: [["player1, 10],["player2", 8]] and teamB: [["player4", 9],["player3", 7]]
What I would prefer is player2 and player3 switch teams. Here's what I tried.
// inside my first for loop
if (3 % i == 0) {
i++;
} else if (4 % i == 0) {
i--;
}
In my brain, this should have worked just fine but wow did it not! I ended up with like 9 players on teamB and 3 on teamA. I fiddled with different variations of this approach with no luck.
Any pointers?
EDIT: For clarification, it can be assumed the data set will be unsorted coming in, and that the length of the data set will always be even. There won't be a team with more players than the other.

I have edited your code so that it will choose first for teamA, then it will choose 2 for teamB, then 2 for teamA and so on.
var data = [
["player1", 10],
["player2", 8],
["player3", 7],
["player4", 9],
["player5", 5],
["player6", 6]
];
var teamA = [];
var teamB = [];
var remaining = [];
for (item in data) {
remaining.push(data[item].slice());
}
var turnA = false;
var counterA = 0,
counterB = 0;
for (i in data) {
var max = 0;
var selection = [, ];
var index = -1;
for (k in remaining) {
if (remaining[k][1] > max) {
selection = remaining[k];
max = remaining[k][1];
index = k;
}
}
remaining.splice(index, 1);
// add first player to teamA and continue
if (i == 0) {
teamA.push(selection);
continue;
}
if (turnA) {
teamA.push(selection);
counterA++;
} else {
teamB.push(selection);
counterB++;
}
if (turnA && counterA == 2) {
counterA = 0;
turnA = false;
} else if (!turnA && counterB == 2) {
counterB = 0;
turnA = true;
}
}

So, the strategy you want to take is the partition problem, as described here: https://en.wikipedia.org/wiki/Partition_problem
You basically want to take the sum of the two teams, based on their players' scores, compare the sum, and assign the current player to the team with the lesser score.
First, we sort the player data.
At the first pass, we randomly select a team to assign the best player. As iterate through the rest of the player pool, we follow the algorithm as described in the partition problem.
I wrote something out really quick as an example of the partition problem in action: https://codepen.io/thenormalsquid/pen/Lymjbb?editors=0001
// stack overflow answer
var data = [["player1", 10], ["player2", 8], ["player3", 7], ["player4", 9]];
var teamA = [];
var teamB = [];
var selectionProbability = 0.5;
var remaining = [];
data.sort(function(playerA, playerB){
if(playerA[1] < playerB[1]) {
return 1;
}
if(playerA[1] > playerB[1]) {
return -1;
}
return 0;
});
var sum = function(team) {
if (team.length === 0) {
return 0;
}
var i,
s = 0;
for(i = 0; i < team.length; i++) {
s += team[i][1];
}
return s;
};
var chooseTeam = function() {
if(Math.random() < selectionProbability) {
return 'teamA';
}
return 'teamB';
};
function assignTeams() {
var i;
for(i = 0; i < data.length; i++) {
var sumA = sum(teamA),
sumB = sum(teamB);
// first pass, we'll have a 50/50 chance
// of placing the best player in either team A or team B
if (i === 0) {
var chosenTeam = chooseTeam();
if (chosenTeam === 'teamA') {
teamA.push(data[i]);
} else {
teamB.push(data[i]);
}
} else if (sumA < sumB) {
teamA.push(data[i]);
} else {
teamB.push(data[i]);
}
}
}
function addPlayerToHtml(player, teamId) {
var li = document.createElement('li'),
text = document.createTextNode(player[0] + ' ' + player[1]);
li.appendChild(text);
document.getElementById(teamId).appendChild(li);
}
var button = document.getElementById('assignPlayers');
button.addEventListener('click', function(){
assignTeams();
teamA.forEach(function(player){
addPlayerToHtml(player, 'teamA');
});
teamB.forEach(function(player){
addPlayerToHtml(player, 'teamB');
});
});

Related

How to understand returning values from recursive function calls?

I am trying to recursively solve a maze using Javascript, how do I return my solution from my recursive function call?
I am attempting to create a maze solver algorithm using recursion, in Javascript. My maze shall follow the following pattern:
let rawMaze =
[
[0, 1, 3],
[0, 1, 0],
[2, 1, 0]
],
Where
0: wall
1: valid path
2: start
3: end
I create an object from the source array,
let maze = []
constructMaze() {
for (let i = 0; i < 3; i++) {
maze[i] = [];
for (let j = 0; j < 3; j++) {
const Cell = {
x: j,
y: i,
state: rawMaze[i][j],
id: uniqueId()
};
this.maze[i].push(Cell);
}
}
console.table(this.maze);
}
I also use a helper function to get the neighbours of any given cell,
getNeighbours(x, y) {
let maze = this.maze;
let neighbours = [];
maze.forEach(row => {
row.forEach(cell => {
if (
(cell.x == x && cell.y == y + 1) ||
(cell.x == x && cell.y == y - 1) ||
(cell.y == y && cell.x == x + 1) ||
(cell.y == y && cell.x == x - 1)
) {
neighbours.push(cell);
}
});
});
return neighbours;
}
The main logic happens in my checkNeighbours function, where I determine the next possible moves and follow them up,
checkNeighbours(neighbours, path, visited) {
let validMoves = [];
neighbours.forEach(potentialMove => {
if (visited.indexOf(potentialMove.id) < 0) {
if (potentialMove.state !== 0) {
validMoves.push(potentialMove);
}
}
});
if (validMoves.length === 0) {
return;
} else {
let finish = validMoves.filter(cell => cell.state === 3);
console.log(finish);
if (finish.length === 1) {
return path;
}
}
validMoves.forEach(validMove => {
path.push(validMove);
visited.push(validMove.id);
this.checkNeighbours(
this.getNeighbours(validMove.x, validMove.y),
path,
visited
);
});
}
I then proceed to try and put this all together and solve the maze,
initSolve(maze) {
let maze = maze;
let start = [];
let paths = [];
let visited = [];
let current = null;
maze.forEach(row => {
row.forEach(cell => {
// Is start?
if ((start.length == 0) & (cell.state == 2)) {
start.push(cell);
visited.push(cell.id);
current = cell;
}
});
});
let result = this.checkNeighbours(
this.getNeighbours(current.x, current.y),
paths,
visited
);
console.log("test", result);
}
My question is the following. Using this very contrived and simple maze configuration, I have stepped through the code and can confirm that my
checkNeighbours()
function will recursively arrive at the end. At that point, the function has an array (the variable path) that contains the correct steps through the maze. How do I return this branch, if you will, from the recursive call? What happens when there are multiple branches?
The only thing I can think of is using a global variable, but I feel this can not be correct.
This is ripped from a React frontend , here is runnable code:
let rawMaze = [
[0, 1, 3],
[0, 1, 0],
[2, 1, 0]
]
let maze = []
function constructMaze() {
let counter = 0
for (let i = 0; i < 3; i++) {
maze[i] = [];
for (let j = 0; j < 3; j++) {
const Cell = {
x: j,
y: i,
state: rawMaze[i][j],
id: counter
};
maze[i].push(Cell);
counter++
}
}
}
function getNeighbours(x, y) {
let maze = this.maze;
let neighbours = [];
maze.forEach(row => {
row.forEach(cell => {
if (
(cell.x == x && cell.y == y + 1) ||
(cell.x == x && cell.y == y - 1) ||
(cell.y == y && cell.x == x + 1) ||
(cell.y == y && cell.x == x - 1)
) {
neighbours.push(cell);
}
});
});
return neighbours;
}
function checkNeighbours(neighbours, path, visited) {
let validMoves = [];
neighbours.forEach(potentialMove => {
if (visited.indexOf(potentialMove.id) < 0) {
if (potentialMove.state !== 0) {
validMoves.push(potentialMove);
}
}
});
if (validMoves.length === 0) {
return;
} else {
let finish = validMoves.filter(cell => cell.state === 3);
console.log(finish);
if (finish.length === 1) {
return path;
}
}
validMoves.forEach(validMove => {
path.push(validMove);
visited.push(validMove.id);
this.checkNeighbours(
this.getNeighbours(validMove.x, validMove.y),
path,
visited
);
});
}
function initSolve() {
let maze = constructMaze()
let start = [];
let paths = [];
let visited = [];
let current = null;
maze.forEach(row => {
row.forEach(cell => {
// Is start?
if ((start.length == 0) & (cell.state == 2)) {
start.push(cell);
visited.push(cell.id);
current = cell;
}
});
});
let result = this.checkNeighbours(
this.getNeighbours(current.x, current.y),
paths,
visited
);
console.log("test", result);
}
Might I recommend adding another class:
function Path() {
this.isValidPath = false;
this.pathArray = [];
}
And also reworking the checkNeighbours function to rename/include these parameters?
checkNeighbours(neighbours, paths, currentPathIndex, visited)
This way, paths could contain an array of Path classes, and you could set the isValidPath flag to true when you found a valid path (assuming you want to also include invalid and valid paths in the array). This would allow you to return all paths (branches). Each branch would be in the paths array at position currentPathIndex, which you'd increment in the code once one path is complete and you want to start searching for another path.
Also, currently the checkNeighbours function appears to do a breadth first search for valid moves. Perhaps if you reworked it into more of a depth-first traversal, then you could add each valid path (and exclude any invalid paths) to the paths array you return.

Refactoring Javascript Function: Array transformation

I have a constant array like this:
const pie_values = [20,10,5,5,10];
The challenge is to transform the above array based on integer input.
(5) => [5, 15, 10, 5, 5, 10]
(21) => [20, 1, 9, 5, 5, 10]
(31) => [20, 10, 1, 4, 5, 10]
An input of 5 takes 5 from the first index of pie_values. But it still leaves 15.
An input of 21 can take 20 from index 0 and 1 from index 2, leaving 9
I think you can see how this is going. So (0) and (50) will return the original pie_values.
Now the challenge is to create a function that does this in a few lines of code, which is build on loops rather than on 5 if statements. In case pie_values is extended upon, the function should still work.
I have an approach working with if statements, however the latter is undoable. What would be a good approach to these kind of problems?
First I defined a helper function:
//Returns summation of pie value
// totalPieValues(1) = 20
// totalPieValues(2) = 30
// totalPieValues(3) = 35
// totalPieValues(4) = 40
// totalPieValues(5) = 50
function totalPieValues(max) {
let result = 0;
for (let i = 0; i < max; i++) {
result += PIE_VALUES[i];
}
return result;
}
Then I worked on a function getPieArray which utilizes the helper function. This is where I am stuck
function getPieArray(wp) {
for (let i = 0; i < PIE_VALUES.length; i++) {
if (wp == 0 || wp == totalPieValues(i)) {
return PIE_VALUES;
}
}
let result = [];
for (let i = 1; i <= PIE_VALUES.length; i++) {
if (wp > totalPieValues(PIE_VALUES.length - i)) {
result.push(PIE_VALUES[i]);
} else if () {
result.push(wp - totalPieValues(3));
} else {
result.push(PIE_VALUES[i] - (value - totalPieValues(3)));
}
}
return result;
}
The code that I have written and works is here:
//Returns array of exact values needed to show in pie chart
export function getPieValues(wp) {
//1 => [1, 19, 10, 5, 5, 10]
//24 => [20, 4, 1, 5, 5, 10]
//31 => [20, 10, 1, 5, 5, 5, 10]
let result;
if (wp == 0) {
result = PIE_VALUES;
} else if (wp < totalPieValues(1)) {
result = [wp - totalPieValues(0), PIE_VALUES[0] - wp, PIE_VALUES[1], PIE_VALUES[2], PIE_VALUES[3], PIE_VALUES[4]];
} else if (wp == totalPieValues(1)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(2)) {
result = [PIE_VALUES[0], wp - totalPieValues(1), PIE_VALUES[1] - (wp - PIE_VALUES[0]), PIE_VALUES[2], PIE_VALUES[3], PIE_VALUES[4]];
} else if (wp == totalPieValues(2)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(3)) {
result = [PIE_VALUES[0], PIE_VALUES[1], wp - totalPieValues(2), PIE_VALUES[2] - (wp - totalPieValues(2)), PIE_VALUES[3], PIE_VALUES[4]];
} else if (wp == totalPieValues(3)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(4)) {
result = [PIE_VALUES[0], PIE_VALUES[1], PIE_VALUES[2], wp - totalPieValues(3), PIE_VALUES[3] - (wp - totalPieValues(3)), PIE_VALUES[4]];
} else if (wp == totalPieValues(4)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(5)) {
result = [PIE_VALUES[0], PIE_VALUES[1], PIE_VALUES[2], PIE_VALUES[3], wp - totalPieValues(4), PIE_VALUES[4] - (wp - totalPieValues(4))];
} else if (wp == totalPieValues(5)) {
result = PIE_VALUES;
}
return result;
}
This is super overkill
You can just iterate through the array and "eat" the index value and continue
function pieArray(inputArray, value){
let copyOfValue = value;
return inputArray.reduce((sum, input, index) => { // <-- index here
copyOfValue -= input;
if(copyOfValue > 0){
sum.push(input);
}else{
sum.push(input+copyOfValue);
sum.push(Math.abs(copyOfValue));
copyOfValue = Number.MAX_VALUE; //Hacky solution, just change value to max value
}
return sum;
}, []);
}
Tests
pieArray([20,10,5,5,10], 5) => [5, 15, 10, 5, 5, 10]
pieArray([20,10,5,5,10], 21) => [20, 1, 9, 5, 5, 10]
pieArray([20,10,5,5,10], 31) => [20, 10, 1, 4, 5, 10]
This is my approach. We iterate over our array, keeping track of our current value - and substracting from it as we push out each element to the output array.
There is 3 cases:
either our current count is >= input, so we just push and move on,
current count is 0, so we just push everything left
current count is < input, but more than 0 - in this case we split.
Here is the code:
function transform(input, array) {
const total = array.reduce((previous, current) => previous + current);
// It wasn't specified what should happen when the input > total, so we will just throw an error.
if (input > total) {
throw new Error('Input cannot be bigger than the sum of elements in the array.');
}
let current = input;
let result = [];
for (let i = 0; i < array.length; i++) {
if (current >= array[i]) {
result.push(array[i]);
current -= array[i];
} else if (current === 0) {
result.push(array[i]);
} else {
result.push(current, array[i] - current);
current = 0;
}
}
return result;
}
Some of these answers are a bit overcomplicated. If you use a recursive function, you can do this in just two lines of code.
const pie_values = [20,10,5,5,10];
// The challenge is to transform the above array based on integer input.
// (5) => [5, 15, 10, 5, 5, 10]
// (21) => [20, 1, 9, 5, 5, 10]
// (31) => [20, 10, 1, 4, 5, 10]
function reshape(num, vals) {
if (num < vals[0]) return [num, vals[0] - num, ...vals.slice(1)];
return [vals[0], ...reshape(num - vals[0], vals.slice(1))];
}
console.log(reshape(5, pie_values))
console.log(reshape(21, pie_values))
console.log(reshape(31, pie_values))
The key is realizing that if the amount you need to take is less than the next value, then you can take it from that next value and the remainder of the array will stay the same.
But if you need to take more than what's available, take as much as you can get from the first value, and then take that much less from the remainder of the array.
EDIT: Note that if the number you give is larger than the sum of all the pie values, this will recurse infinitely (leading to a stack overflow). To be totally safe, you should ensure that the value is less than the total sum before calling reshape.
You want something like that ? (Works with the examples you've given)
function getPieValues(integer_input) {
"use strict";
let final_arr = pie_values,
array_sum = pie_values.reduce((pv, cv) => pv + cv , 0);
if(integer_input !== 0 && integer_input !== array_sum) { // For the cases 50 and 0, the array won't be modified
for(let i = 0; i < pie_values.length; i++) {
if(pie_values[i] < integer_input) { // if the prompted number is bigger than the current value, we keep up
integer_input -= pie_values[i];
} else { // When it becomes smaller, we add the remainder at the front of the current value, then we modify the next value, and finally we break it so that it doesn't happen next
final_arr.splice(i, 0, integer_input);
final_arr[i+1] -= integer_input;
break;
}
}
}
return final_arr;
}
Edit : Made it a function, and made it work with 0 and 50 (sorry, first post ;-) )
This one's pretty simple and efficient. It doesn't iterate the whole array, only up to the point it needs to.
const pie_values = [20,10,5,5,10];
function pied(n) {
var i = 0;
var total = pie_values[0];
while (total < n && i < pie_values.length) {
i++;
total += pie_values[i];
}
if (i < pie_values.length) {
var diff = total - n;
if (diff > 0 && n > 0) {
return [].concat(
pie_values.slice(0, i), // the part of the array up to i
pie_values[i] - diff, // the amount we used of the last element we needed
diff, // the amount left over
pie_values.slice(i + 1) // the rest of the array after i
);
} else {
// just return a copy of the original array
return pie_values.slice();
}
} else {
// n was greater than the total of all elements of the array
return "went over";
}
}
console.log(pied(5));
Using vanilla Javascript for-loop
Look at this code snippet
const pie_values = [20, 10, 5, 5, 10];
var fn = (input) => {
let array = [];
for (var i = 0; i < pie_values.length; i++) {
var n = pie_values[i];
let calc = n - input;
if (calc > 0) {
array.push(n - calc); // Push how many used, i.e n = 20, input = 10.
array.push(calc); // Push the remaining after subtraction.
array = array.concat(pie_values.slice(i + 1)); // Add the remaining values from 'pie_values'
return array;
} else {
array.push(n); // Push all this number because was insufficient, i.e n = 20, input = 30
input = Math.abs(calc); // Remaining for the next iteration.
}
}
return array;
};
console.log(fn(5));
console.log(fn(21));
console.log(fn(31));
console.log(fn(0));
console.log(fn(50));
const pie_values = [20,10,5,5,10];
function rebaseArr(input){
var retArr = [];
var total = 0;
let isDone = false;
for(var i in pie_values){
let currentVal = pie_values[i];
total += currentVal;
if(total > input && !isDone){
let rem = total - input;
let rem1 = currentVal - rem;
rem1 !== 0 ? retArr.push(rem1) : 0;
retArr.push(rem);
isDone = true;
} else {
retArr.push(currentVal);
}
}
return retArr;
}
console.log(rebaseArr(31));
console.log(rebaseArr(1));
console.log(rebaseArr(10));
Can you please try with above code.
Hope it helps :)
I wouldn't normally advise recursion in JS however just for fun you may implement an Haskellesque pattern matching by using spread and rest operators through destructuring and may come up with something like below;
It wasn't clear to me what to do when the difference is zero so being a remarkably lazy person i choose to do nothing. (Last test won't return [20,10,5,0,5,10])
var extend = ([x,...xs],n) => n && x ? x > n ? [n, x-n, ...xs]
: [x, ...extend(xs, n-x)]
: [x,...xs],
pvs = [20,10,5,5,10];
console.log(extend(pvs,5));
console.log(extend(pvs,21));
console.log(extend(pvs,31));
console.log(extend(pvs,35));
.as-console-wrapper {
max-height : 100% !important
}

javascript check if array contains multiple elements in a row

I would like to know if its possible to search an array for multiple items which are in a row, something similar to below. I have done it with separate includes, but this does not allow me to tell if the elements are in a row or near each other.
The array is not sorted and the numbers have to be in a defined order. Near being in a row, specifically 3. So (23,34,45) being searched within (12,23,45,34) would be false.
Thanks
var num = [];
num.push(12,23,34,45,56,67,78,89,90);
if(num.includes(23,34,45)){
print('found');
}
One more way using ES6 Set() feature:
var arr = [12,23,34,12,45,56,67,78,89,90];
var set = new Set();
arr.forEach(function(i){ set.add(i) });
var foundCount = 0;
var func = function(a){
a.forEach(function(item) {
var oldLength = set.size;
set.add(item);
var newLength = set.size;
if(oldLength === newLength) {
foundCount++;
}
});
console.log('found ' + foundCount)
}
var arr2 = [12, 34, 45];
func(arr2);
This works, I hope I understood your question correctly.
function foo(num1, num2, num3, arr) {
var exists = false;
for(var i = 0; i < arr.length; i++) {
if(arr[i] == num1 && arr[i+1] == num2 && arr[i+2] == num3) {
exists = true;
}
}
console.log(exists);
}
var array = [12,23,34,45,56,67,78,89,90];
foo(23,34,45,array);

looping infinitely through multiple arrays at the same time until a condition is met

I'm trying to find least common multiples of the two numbers given [3,5] and return only the number that's divisible by all the number in the range of the two numbers... for example:
The given array of two numbers --> let arr = [3,5];
The first number Multiples should be as follow:
[3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57,60];
The second number Multiples should be as follow:
[5,10,15,20,25,30,35,40,45,50,55,60];
The Least common multiples should be as follows:
[15,30,45,60];
the only that is divisible by all the number in the range is 60.
This is my approach to solve this problem but I want to know what's wrong with my code below (PLEASE EXPLAIN 'cause I'm tired of guessing):
let arr = [3, 5];
let arrRange = []; // [3, 4, 5]
// creating a loop to create the range
for (var i = arr[0]; i <= arr[1]; i++) {
arrRange.push(i);
}
let f = arr[0], s = arr[1], c = 0, result = 0, firstMultiples = [], secondMultiples = [], leastCommonMultiples = [];
// This function is made if the number least Common number is divisible by all the numbers in the "arrRange"
function isDivisible(num) {
for(var i = 0; i < arrRange.length; i++) {
if(num % arrRange[i] != 0) {
return false;
}
}
return true;
}
while(true) {
firstMultiples.push(f);
secondMultiples.push(s);
f = f + arr[0];
s = s + arr[1];
let vals = secondMultiples.values();
for(let val of vals){
if( firstMultiples.includes(val) ) {
leastCommonMultiples.push(val);
}
}
let cmlVals = leastCommonMultiples.values();
for(let cmlVal of cmlVals){
if(isDivisible(cmlVal)) {
result += cmlVal;
break;
}
}
c++;
}
console.log(result);
To fix it, change the while-loop from while (true) {/*code*/}; to
while(isDivisible(cmlVal) == true) {/*code*/}; and remove the
if(isDivisible(cmlVal)) {/*code*/ break;}.

Javascript implementation of the inversion-counting with merge-sort algorithm

i am trying to implement the inversion-counting using merge sort algorithm in javascript. I found description and pseudo-code on this site.
My implementation looks like this:
var mergeAndCount, sortAndCount;
/*
the merging routine
#param List1 the first list to be merged
#param List2 the second list to be merged
*/
mergeAndCount = function(List1, List2) {
var count, outputList;
outputList = [];
count = 0;
while (List1.length > 0 || List2.length > 0) {
outputList.push(Math.min(List1[0], List2[0]));
if (List2[0] < List1[0]) {
count += List1.length;
List2.shift();
} else {
List1.shift();
}
}
outputList = outputList.concat(List1.concat(List2));
return {
'count': count,
'list': outputList
};
};
/*
count inversion algorithm
#param List the sequence to be sorted
*/
sortAndCount = function(List) {
var List1, List2, mergeOut, output1, output2;
if (List.length < 2) {
return {
'count': 0,
'list': List
};
} else {
List1 = List.splice(0, List.length / 2);
List2 = List;
output1 = sortAndCount(List1);
output2 = sortAndCount(List2);
mergeOut = mergeAndCount(List1, List2);
return {
'count': output1.count + output2.count + mergeOut.count,
'list': mergeOut.list
};
}
};
I wanted to test it on Jsfiddle here, but it crashes (too much memory used). Somehow it works for the inupt [1, 3, 2] but not for other. I am not sure what is going wrong, if my implementation or the original pseudocode is false.
Error 1 : infinite loop
The while goes on for a very long time when it starts to compare numbers with undefined. If List1.length is 0, the comparison List2[0] < List1[0] will always be false, resulting in List1.shift() which changes nothing.
Replace:
while (List1.length > 0 || List2.length > 0) {
With:
while (List1.length > 0 && List2.length > 0) {
Error 2 : manipulating arrays
You alter the arrays and then use what you expect to be their initial values. At the begining of each function you should copy the arrays (using slice is the fastest way).
Error 3 : ignoring output of sortAndCount
Replace:
mergeOut = mergeAndCount(List1, List2);
With:
mergeOut = mergeAndCount(output1.list, output2.list);
Correct solution:
var mergeAndCount, sortAndCount;
/*
the merging routine
#param List1 the first list to be merged
#param List2 the second list to be merged
*/
mergeAndCount = function(List1, List2) {
List1 = List1.slice();
List2 = List2.slice();
var count = 0;
var outputList = [];
while (List1.length > 0 && List2.length > 0) {
outputList.push(Math.min(List1[0], List2[0]));
if (List2[0] < List1[0]) {
count += List1.length;
List2.shift();
} else {
List1.shift();
}
}
outputList = outputList.concat(List1.concat(List2));
return {
'count': count,
'list': outputList
};
};
/*
count inversion algorithm
#param List the sequence to be sorted
*/
sortAndCount = function(List) {
List = List.slice();
var List1, List2, mergeOut, output1, output2;
if (List.length < 2) {
return {
'count': 0,
'list': List
};
} else {
List1 = List.splice(0, Math.floor(List.length / 2));
List2 = List;
output1 = sortAndCount(List1);
output2 = sortAndCount(List2);
mergeOut = mergeAndCount(output1.list, output2.list);
return {
'count': output1.count + output2.count + mergeOut.count,
'list': mergeOut.list
};
}
};
console.clear();
var r = sortAndCount([1,3,4,2]);
console.log('RESULT',r.list);
DEMO: http://jsbin.com/UgUYocu/2/edit
As pointed out, the problem was || instead of &&. Here's an implementation that seems to work (to make things interesting, it returns a list of inversions instead of simply counting them):
sort_and_count = function(L) {
if (L.length < 2)
return [[], L];
var m = L.length >> 1;
var na = sort_and_count(L.slice(0, m));
var nb = sort_and_count(L.slice(m));
var nc = merge_and_count(na[1], nb[1]);
return [[].concat(na[0], nb[0], nc[0]), nc[1]];
}
merge_and_count = function(a, b) {
var inv = [], c = [];
while(a.length && b.length) {
if(b[0] < a[0]) {
a.forEach(function(x) { inv.push([x, b[0]])});
c.push(b.shift());
} else {
c.push(a.shift());
}
}
return [inv, c.concat(a, b)];
}
nn = sort_and_count([2, 4, 1, 3, 5])
// [[[2,1],[4,1],[4,3]],[1,2,3,4,5]]
For completeness, here's the quadratic algorithm:
inversions = function(L) {
return L.reduce(function(lst, a, n, self) {
return self.slice(n).filter(function(b) {
return b < a;
}).map(function(b) {
return [a, b];
}).concat(lst);
}, []);
}
inversions([2, 4, 1, 3, 5])
// [[4,1],[4,3],[2,1]]

Categories

Resources