JavaScript shuffling algorithm explanation - javascript

I've found shuffling algorithm seems work fine.
const questions = [
{ name: "Ananda or Nalanda" },
{ name: "Sunny or Rainy" },
{ name: "Facebook or Instagram" },
{ name: "IOS or Android" },
{ name: "Mc or KFC" }
];
questions.sort(() => Math.random() - 0.5)
questions.forEach(e=>{
console.log(e.name)
})
But I couldn't think how this work in the syntax. I know that Math.random() will generate a number between 0 and 1. Also sort is standard function to sort. But how do these two functions shuffle my array? Why does it deduct 0.5 from Math.random()?

The return value from the .sort callback is expected to be a positive number, 0, or a negative number. So, subtracting 0.5 from a variable with a range of [0, 1) results in a range of [-0.5, 0.5) - an equal distribution of randomly sorting a before b, and of sorting b before a (where a and b are the elements being compared). This kind of randomly sorts the array, by randomly determining whether an a comes before or after a b.
If you didn't subtract 0.5, or subtracted something other than 0.5, the results would be significantly biased.
BUT, this is not a good way to randomly sort an array; the results here will be somewhat biased as well:
// an array of 'a' to 'f'
const questions = Array.from(
{ length: 6 },
(_, i) => String.fromCharCode(i + 97)
);
const positionFrequency = {};
for (let i = 0; i < 1e5; i++) {
const sorted = questions.slice().sort(() => Math.random() - 0.5);
sorted.forEach((char, i) => {
if (!positionFrequency[char]) {
positionFrequency[char] = {};
}
positionFrequency[char][i] = (positionFrequency[char][i] || 0) + 1;
});
}
console.log(positionFrequency);
Please run the snippet - it is very biased! In Chrome, a occurs in the first position some 28% of the time, despite the fact that it should occur there only 1/6th (16.667%) of the time. In Firefox 56, it's even more biased than that.
This is because the sorting algorithm is not stable - the results depend on which elements are compared against which other elements first (which is implementation-dependent). You can read more details about how exactly this kind of random-sorting is biased here:
http://www.robweir.com/blog/2010/02/microsoft-random-browser-ballot.html

Array.sort sorts an array given a compare function. The sort function compares all the items in the array using the compare function to determine which ones should go before other ones. The compare function must return a negative, zero, or positive value. It uses this value to determine which value should go first.
For example, when comparing values a and b, the sort function will call compare_function(a, b) and if it returns a negative value, sort will place a before b in the final sorted array. If the compare function returns a positive value, sort will place a after b.
So in your example, Math.random() - 0.5 is the compare function. Because Math.random() normally returns a value between 0 and 1, Math.random() - 0.5 will return a random number between -0.5 and 0.5. Therefore, the chance that the compare function (which is Math.random() - 0.5) will return a positive number is the same as the chance that the compare function will return a negative value.
In other words, a random number between -0.5 and +0.5 is used to determine whether an arbitrary item a in your array goes before or after an item b. And because the chances of a positive versus negative number being used are the same, the chances of a before b versus b before a, in the sorted array, are the same.
I hope this answer is helpful!

Related

How to add every combination of numbers in an array

I'm taking in an array of numbers [4, 6, 23, 10, 1, 3] I need to return true if any combinations of numbers in the array add up to the largest number in the array. So the example above should return true because 3 + 4 + 6 + 10 = 23
My thought behind this problem is I should first put the array in numerical order then make a new array with just the numbers I'm adding because I'm not adding the largest number. Then I need some method that says "If any combination of adding these numbers together equals the largest number in the original array return true". This is the code I've written so far, but I'm stuck on the for loop.. Any help would be very much appreciated!
function ArrayChallenge(arr){
let order = arr.sort(function(a, b){return a-b})
let addThese = order.slice(0,order.length-1)
for(i = 0; i < addThese.length-1; i++){
return true
}
}
console.log(ArrayChallenge([3,5,-1,8,12]))
It seems like a trick question. Here's the answer:
function ArrayChallenge(arr){
return true
}
Because the sum of just the largest number is always equal to the largest number.
If this solution is somehow not allowed, see subset sum problem:
there is a multiset S of integers and a target-sum T, and the question is to decide whether any subset of the integers sum to precisely T.
The most straightforward solution to that problem is with recursion; something like this:
function subsetSum(arr, sum) {
if (arr.length == 0) {
// No numbers left in the array. The only sum we can make is 0.
// If that is the sum we are seeking, great!
return sum == 0
} else {
// We will try both using and not using the first number in the array.
// The rest is passed to the recursive call.
const [current, ...remaining] = arr
return (
// Exclude current number, and recurse. We have to make the full
// sum with the remaining numbers.
subsetSum(remaining, sum) ||
// Include current number, meaning we have sum - current left
// to make with the remaining numbers.
subsetSum(remaining, sum - current)
)
}
}

Why is my for loop breaking earlier than expected?

I am trying to solve a problem on leetCode:
Given an unsorted integer array nums, return the smallest missing positive integer.
This is the code I came up with
var firstMissingPositive = function(nums) {
nums.sort();
let x = 1; //this is to compare the elements of nums
for (let num in nums) {
if (nums[num] <= 0) continue; //because anything less than 1 does not matter
else if (nums[num] != x) break; //if x is not present, x is the answer
else x++; // if x is present, x should increment and check for the next integer
}
return x;
};
This code works 106/173 testcases. It does not pass the following case, which looks very simple -
nums = [1,2,3,4,5,6,7,8,9,20];
The output I get is 3, whereas the expected output is 10.
I'm not looking for the right solution to the problem. I'm just curious why this seemingly simple test fails. I do not understand why my loop breaks at 3 when it passes 1 and 2. Please help!
Here's the root cause of your problem (mdn):
The sort() method sorts the elements of an array in place and returns
the sorted array. The default sort order is ascending, built upon
converting the elements into strings, then comparing their sequences
of UTF-16 code units values.
So what you get after sort is [1, 2, 20, 3, ...], as '20' string precedes '3' string. One possible way to fix this it to force sorting by numeric value:
nums.sort((a, b) => a - b);

Javascript sort method for arrays [duplicate]

How does the following code sort this array to be in numerical order?
var array=[25, 8, 7, 41]
array.sort(function(a,b){
return a - b
})
I know that if the result of the computation is...
Less than 0: "a" is sorted to be a lower index than "b".
Zero: "a" and "b" are considered equal, and no sorting is performed.
Greater than 0: "b" is sorted to be a lower index than "a".
Is the array sort callback function called many times during the course of the sort?
If so, I'd like to know which two numbers are passed into the function each time. I assumed it first took "25"(a) and "8"(b), followed by "7"(a) and "41"(b), so:
25(a) - 8(b) = 17 (greater than zero, so sort "b" to be a lower index than "a"): 8, 25
7(a) - 41(b) = -34 (less than zero, so sort "a" to be a lower index than "b": 7, 41
How are the two sets of numbers then sorted in relation to one another?
Please help a struggling newbie!
Is the array sort callback function called many times during the course of the sort?
Yes
If so, I'd like to know which two numbers are passed into the function each time
You could find out your self with:
array.sort((a,b) => {
console.log(`comparing ${a},${b}`);
return a > b ? 1
: a === b ? 0
: -1;
});
EDIT
This is the output I've got:
25,8
25,7
8,7
25,41
The JavaScript interpreter has some kind of sort algorithm implementation built into it. It calls the comparison function some number of times during the sorting operation. The number of times the comparison function gets called depends on the particular algorithm, the data to be sorted, and the order it is in prior to the sort.
Some sort algorithms perform poorly on already-sorted lists because it causes them to make far more comparisons than in the typical case. Others cope well with pre-sorted lists, but have other cases where they can be "tricked" into performing poorly.
There are many sorting algorithms in common use because no single algorithm is perfect for all purposes. The two most often used for generic sorting are Quicksort and merge sort. Quicksort is often the faster of the two, but merge sort has some nice properties that can make it a better overall choice. Merge sort is stable, while Quicksort is not. Both algorithms are parallelizable, but the way merge sort works makes a parallel implementation more efficient, all else being equal.
Your particular JavaScript interpreter may use one of those algorithms or something else entirely. The ECMAScript standard does not specify which algorithm a conforming implementation must use. It even explicitly disavows the need for stability.
Pairs of values are compared, one pair at a time. The pairs that are compared are an implementation detail--don't assume they will be the same on every browser. The callback can be anything (so you can sort strings or Roman numerals or anything else where you can come up with a function that returns 1,0,-1).
One thing to keep in mind with JavaScript's sort is that it is not guaranteed to be stable.
Deeply Knowledge
If the result is negative a is sorted before b.
If the result is positive b is sorted before a.
If the result is 0 no changes are done with the sort order of the two values.
NOTE:
This code is the view inside of the sort method step by step.
OUTPUT:
let arr = [90, 1, 20, 14, 3, 55];
var sortRes = [];
var copy = arr.slice(); //create duplicate array
var inc = 0; //inc meant increment
copy.sort((a, b) => {
sortRes[inc] = [ a, b, a-b ];
inc += 1;
return a - b;
});
var p = 0;
for (var i = 0; i < inc; i++) {
copy = arr.slice();
copy.sort((a, b) => {
p += 1;
if (p <= i ) {
return a - b;
}
else{
return false;
}
});
p = 0;
console.log(copy +' \t a: '+ sortRes[i][0] +' \tb: '+ sortRes[i][1] +'\tTotal: '+ sortRes[i][2]);
}
To help clarify the behavior of Array#sort and its comparator, consider this naive insertion sort taught in beginning programming courses:
const sort = arr => {
for (let i = 1; i < arr.length; i++) {
for (let j = i; j && arr[j-1] > arr[j]; j--) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]];
}
}
};
const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array);
console.log("" + array);
Ignoring the choice of insertion sort as the algorithm, focus on the hardcoded comparator: arr[j-1] > arr[j]. This has two problems relevant to the discussion:
The > operator is invoked on pairs of array elements but many things you might want to sort such as objects don't respond to > in a reasonable way (the same would be true if we used -).
Even if you are working with numbers, oftentimes you want some other arrangement than the ascending sort that's been baked-in here.
We can fix these problems by adding a comparefn argument which you're familiar with:
const sort = (arr, comparefn) => {
for (let i = 1; i < arr.length; i++) {
for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]];
}
}
};
const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array, (a, b) => a - b);
console.log("" + array);
sort(array, (a, b) => b - a);
console.log("" + array);
const objArray = [{id: "c"}, {id: "a"}, {id: "d"}, {id: "b"}];
sort(objArray, (a, b) => a.id.localeCompare(b.id));
console.log(JSON.stringify(objArray, null, 2));
Now the naive sort routine is generalized. You can see exactly when this callback is invoked, answering your first set of concerns:
Is the array sort callback function called many times during the course of the sort? If so, I'd like to know which two numbers are passed into the function each time
Running the code below shows that, yes, the function is called many times and you can use console.log to see which numbers were passed in:
const sort = (arr, comparefn) => {
for (let i = 1; i < arr.length; i++) {
for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]];
}
}
};
console.log("on our version:");
const array = [3, 0, 4, 5];
sort(array, (a, b) => console.log(a, b) || (a - b));
console.log("" + array);
console.log("on the builtin:");
console.log("" +
[3, 0, 4, 5].sort((a, b) => console.log(a, b) || (a - b))
);
You ask:
How are the two sets of numbers then sorted in relation to one another?
To be precise with terminology, a and b aren't sets of numbers--they're objects in the array (in your example, they're numbers).
The truth is, it doesn't matter how they're sorted because it's implementation-dependent. Had I used a different sort algorithm than insertion sort, the comparator would probably be invoked on different pairs of numbers, but at the end of the sort call, the invariant that matters to the JS programmer is that the result array is sorted according to the comparator, assuming the comparator returns values that adhere to the contract you stated (< 0 when a < b, 0 when a === b and > 0 when a > b).
In the same sense that I have the freedom to change my sort's implementation as long as I don't breach my specification, implementations of ECMAScript are free to choose the sort implementation within the confines of the language specification, so Array#sort will likely produce different comparator calls on different engines. One would not write code where the logic relies on some particular sequence of comparisons (nor should the comparator produce side effects in the first place).
For example, the V8 engine (at the time of writing) invokes Timsort when the array is larger than some precomputed number of elements and uses a binary insertion sort for small array chunks. However, it used to use quicksort which is unstable and would likely give a different sequence of arguments and calls to the comparator.
Since different sort implementations use the return value of the comparator function differently, this can lead to surprising behavior when the comparator doesn't adhere to the contract. See this thread for an example.
Is the array sort callback function called many times during the course of the sort?
Yes, that's exactly it. The callback is used to compare pairs of elements in the array as necessary to determine what order they should be in. That implementation of the comparison function is not atypical when dealing with a numeric sort. Details in the spec or on some other more readable sites.
Is the array sort callback function called many times during the course of the sort?
Since this is a comparison sort, given N items, the callback function should be invoked on average (N * Lg N) times for a fast sort like Quicksort. If the algorithm used is something like Bubble Sort, then the callback function will be invoked on average (N * N) times.
The minimum number of invocations for a comparison sort is (N-1) and that is only to detect an already sorted list (i.e. early out in Bubble Sort if no swaps occur).
Is the array sort callback function called many times during the course of the sort?
Yes
If so, I'd like to know which two numbers are passed into the function each time.
a: The first element for comparison.
b: The second element for comparison.
In the following example, a will be "2" and b will be "3" in the first iteration
How are the two sets of numbers then sorted in relation to one another?
Elements are sorted according to the return value of the compare function.
greater than 0: sort a after b
less than 0: sort a before b
equal to 0: keep original order of a and b
Here is an example
var arr = [3, 2, 1, 5, 4, 6, 7, 9, 8, 10];
console.log(arr.sort((a, b) => {
console.log(a - b, a, b);
//b-a if sorting in decending order
return a - b;
}));

Javascript .sort() function: what are return 0, return -1 and return 1 for?

I am studying Javascript and currently learning the .sort() function for arrays. I understood that it can take either no argument or one between a-b and b-a.
What I don't understand however is the use of return 0, return -1 and return 1. Here is an example (source: http://www.codewars.com/kata/572df796914b5ba27c000c90) :
var arr=[1,2,3,4,5,6,100,999]
arr.sort((a,b)=>{
if (a%2==b%2) return a-b;
if (a%2>b%2) return -1;
return 1;
})
console.log(arr)
//output: [ 1, 3, 5, 999, 2, 4, 6, 100 ]
I understand what it's supposed to do, i.e. separate odd and even numbers and sort them in ascending order. But what is the meaning of return -1 and return 1? Can someone walk me through this function step by step?
I tried to play with the code and change some values, for example change return -1 to return 0, to try to understand how it could work, but I still don't get it.
Where can I find resources with details about that return element?
According to the sort docs:
If the parameter functionComparison is supplied, the elements of the
array are sorted according to the return value of the comparison
function. If a and bare two elements to compare, then:
If functionComparison(a, b) is less than 0, we sort a with an index
less than b( a will be ranked before b)
If functionComparison(a, b) returns 0, we leave a and b unchanged
relative to each other, but sorted with respect to all the other
elements. Note: The ECMAScript standard does not guarantee this
behavior, so all browsers (eg Mozilla versions prior to 2003) do not
respect this. If functionComparison(a, b) is greater than 0, we sort b
with an index less than a.
functionComparison(a, b) must always return the same result from the
same pair of arguments. If the function returns inconsistent results,
then the order in which the items are sorted is not defined.
Now if a > b, returning 1 or a positive value is one and the same thing, similarly, if a < b then returning -1 or the difference is the same. If both are equal the difference is 0 and hence return 0
Where can I find resources with details about that return element?
return value is for the comparator function.
As per spec
If comparefn is not undefined, it should be a function that accepts
two arguments x and y and returns a negative value if x < y, zero if x
= y, or a positive value if x > y.

Why is this sorting algorithm producing inconsistent results across browsers?

I'm trying to implement a (pretty simple indeed) absolute deviation sorting algorithm in Javascript. Absolute deviation is defined as the absolute value of the difference between one element and the average of all elements. For example, given the elements 1, 4, 5 and 9, the average would be (1 + 4 + 5 + 9) / 4 = 4.75, and so the absolute deviation of each element would be calculated as follows:
absDev(1) = |1 - 4.75| = 3.75
absDev(4) = |4 - 4.75| = 0.75
absDev(5) = |5 - 4.75| = 0.25
absDev(9) = |9 - 4.75| = 4.25
Sorting the elements by ascending absolute deviance would hence give the sequence 5, 4, 1, 9. So far so good, my current Javascript implementation is giving me different results in different browsers.
Here it is: http://jsfiddle.net/WVvuu/
In Firefox and Safari, I'm getting the expected result 5, 4, 1, 9
In Chrome and Opera, I'm getting 4, 5, 1, 9
In IE 10, I'm getting 1, 4, 5, 9
I guess there must be probably some very simple mistake in my code but I can't seem to find it. I'd like to understand what's wrong with it and why I'm getting a different result when I change my browser. I'd appreciate it if someone could kindly explain what I'm missing. Again, this is the code:
var array = [1, 4, 5, 9];
function absDev(x) {
return Math.abs(x - average(array));
}
function average(array) {
var sum = array.reduce(function(previousValue, currentValue) {
return previousValue + currentValue;
}, 0);
return sum / array.length;
}
array.sort(function(x, y) {
return absDev(x) - absDev(y);
});
alert("Sorted array: " + array);
I suspect it's because the state of the array while the sort is going on isn't necessarily consistent. You really shouldn't be recomputing the average on each comparison anyway:
array.sort(function(array) {
var avg = array.reduce(function(previousValue, currentValue) {
return previousValue + currentValue;
}, 0);
avg /= array.length;
return function(x, y) {
return Math.abs(x - avg) - Math.abs(y - avg);
};
}(array));
See if that works better. (edit — it gives me the correct answer in Chrome.)
In more detail, my suspicion is that the sort functions where you're seeing weird results may perform swaps on the array in place, and there may be intervals during which one or more original array values is either missing or replicated while the sort mechanism is doing its thing. Thus, your averaging function sees an array (sometimes) with a different list of values, meaning that the average comes out different (sometimes).
The sorting function is recalculating the average of the array for every step of the sort. Different browsers may not keep the array fully intact during the sort process. You can see the effects of this if you add console.log(previousValue, currentValue); into the array.reduce function.
You need to calculate the average of the array first, and store that into a variable. Then pass that variable into the sort function. Here are the changes I made to your code:
var array = [1, 4, 5, 9];
var mean = average(array);
function absDev(x, mean) {
return Math.abs(x - mean);
}
function average(array) {
var sum = array.reduce(function(previousValue, currentValue) {
console.log(previousValue, currentValue);
return previousValue + currentValue;
}, 0);
return sum / array.length;
}
array.sort(function(x, y) {
return absDev(x, mean) - absDev(y, mean);
});
console.log("Sorted array: " + array);
The array state while the sort is in progress is specified to be implementation defined, so you cannot trust that the items are in the array in a particular way during the sort. You must pre-calculate the average before starting the sorting process.
Reference
http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.11
Quote
Perform an implementation-dependent sequence of calls to the [[Get]] ,
[[Put]], and [[Delete]] internal methods of obj

Categories