You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					115 lines
				
				3.5 KiB
			
		
		
			
		
	
	
					115 lines
				
				3.5 KiB
			| 
											3 years ago
										 | /* -*- Mode: js; js-indent-level: 2; -*- */ | ||
|  | /* | ||
|  |  * Copyright 2011 Mozilla Foundation and contributors | ||
|  |  * Licensed under the New BSD license. See LICENSE or: | ||
|  |  * http://opensource.org/licenses/BSD-3-Clause
 | ||
|  |  */ | ||
|  | 
 | ||
|  | // It turns out that some (most?) JavaScript engines don't self-host
 | ||
|  | // `Array.prototype.sort`. This makes sense because C++ will likely remain
 | ||
|  | // faster than JS when doing raw CPU-intensive sorting. However, when using a
 | ||
|  | // custom comparator function, calling back and forth between the VM's C++ and
 | ||
|  | // JIT'd JS is rather slow *and* loses JIT type information, resulting in
 | ||
|  | // worse generated code for the comparator function than would be optimal. In
 | ||
|  | // fact, when sorting with a comparator, these costs outweigh the benefits of
 | ||
|  | // sorting in C++. By using our own JS-implemented Quick Sort (below), we get
 | ||
|  | // a ~3500ms mean speed-up in `bench/bench.html`.
 | ||
|  | 
 | ||
|  | /** | ||
|  |  * Swap the elements indexed by `x` and `y` in the array `ary`. | ||
|  |  * | ||
|  |  * @param {Array} ary | ||
|  |  *        The array. | ||
|  |  * @param {Number} x | ||
|  |  *        The index of the first item. | ||
|  |  * @param {Number} y | ||
|  |  *        The index of the second item. | ||
|  |  */ | ||
|  | function swap(ary, x, y) { | ||
|  |   var temp = ary[x]; | ||
|  |   ary[x] = ary[y]; | ||
|  |   ary[y] = temp; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Returns a random integer within the range `low .. high` inclusive. | ||
|  |  * | ||
|  |  * @param {Number} low | ||
|  |  *        The lower bound on the range. | ||
|  |  * @param {Number} high | ||
|  |  *        The upper bound on the range. | ||
|  |  */ | ||
|  | function randomIntInRange(low, high) { | ||
|  |   return Math.round(low + (Math.random() * (high - low))); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * The Quick Sort algorithm. | ||
|  |  * | ||
|  |  * @param {Array} ary | ||
|  |  *        An array to sort. | ||
|  |  * @param {function} comparator | ||
|  |  *        Function to use to compare two items. | ||
|  |  * @param {Number} p | ||
|  |  *        Start index of the array | ||
|  |  * @param {Number} r | ||
|  |  *        End index of the array | ||
|  |  */ | ||
|  | function doQuickSort(ary, comparator, p, r) { | ||
|  |   // If our lower bound is less than our upper bound, we (1) partition the
 | ||
|  |   // array into two pieces and (2) recurse on each half. If it is not, this is
 | ||
|  |   // the empty array and our base case.
 | ||
|  | 
 | ||
|  |   if (p < r) { | ||
|  |     // (1) Partitioning.
 | ||
|  |     //
 | ||
|  |     // The partitioning chooses a pivot between `p` and `r` and moves all
 | ||
|  |     // elements that are less than or equal to the pivot to the before it, and
 | ||
|  |     // all the elements that are greater than it after it. The effect is that
 | ||
|  |     // once partition is done, the pivot is in the exact place it will be when
 | ||
|  |     // the array is put in sorted order, and it will not need to be moved
 | ||
|  |     // again. This runs in O(n) time.
 | ||
|  | 
 | ||
|  |     // Always choose a random pivot so that an input array which is reverse
 | ||
|  |     // sorted does not cause O(n^2) running time.
 | ||
|  |     var pivotIndex = randomIntInRange(p, r); | ||
|  |     var i = p - 1; | ||
|  | 
 | ||
|  |     swap(ary, pivotIndex, r); | ||
|  |     var pivot = ary[r]; | ||
|  | 
 | ||
|  |     // Immediately after `j` is incremented in this loop, the following hold
 | ||
|  |     // true:
 | ||
|  |     //
 | ||
|  |     //   * Every element in `ary[p .. i]` is less than or equal to the pivot.
 | ||
|  |     //
 | ||
|  |     //   * Every element in `ary[i+1 .. j-1]` is greater than the pivot.
 | ||
|  |     for (var j = p; j < r; j++) { | ||
|  |       if (comparator(ary[j], pivot) <= 0) { | ||
|  |         i += 1; | ||
|  |         swap(ary, i, j); | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     swap(ary, i + 1, j); | ||
|  |     var q = i + 1; | ||
|  | 
 | ||
|  |     // (2) Recurse on each half.
 | ||
|  | 
 | ||
|  |     doQuickSort(ary, comparator, p, q - 1); | ||
|  |     doQuickSort(ary, comparator, q + 1, r); | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Sort the given array in-place with the given comparator function. | ||
|  |  * | ||
|  |  * @param {Array} ary | ||
|  |  *        An array to sort. | ||
|  |  * @param {function} comparator | ||
|  |  *        Function to use to compare two items. | ||
|  |  */ | ||
|  | exports.quickSort = function (ary, comparator) { | ||
|  |   doQuickSort(ary, comparator, 0, ary.length - 1); | ||
|  | }; |