-1

Possible Duplicate:
How to randomize a javascript array?

I need to implement a randomized binary search tree (R-BST) out of an array so that the sorted array gives O(n lg n) average time and not the O(n^2) which is the worst case time if the arrays are already sorted or reverse sorted . Now the two steps are :

  1. Randomly permute the array A.
  2. Call BST sort (A) .

How do I go about doing the first step JavaScript? I want it so that each of the n! permutations is equally likely to happen . I believe the way to do this in Java is Collections.shuffle say something like :

Integer[] arr = new Integer[10]; 

for (int i = 0; i < arr.length; i++) { 
    arr[i] = i; 
} 

Collections.shuffle(Arrays.asList(arr)); 

for (int i = 0; i < arr.length; i++) { 
    System.out.print(arr[i] + " "); 
} 

How would I do this in Javascript ? I can use jQuery.

Community
  • 1
  • 1
Geek
  • 26,489
  • 43
  • 149
  • 227
  • Maybe some Googling would reveal [this](http://jsfromhell.com/array/shuffle) – Adi Aug 20 '12 at 08:16
  • Just a small thing that might help you in the future: in java you can print an array by using `System.our.println(Arrays.toString(arr));` – amit Aug 20 '12 at 08:20

2 Answers2

1

Just use .sort with random comparer:

var comparer = function(a,b) {
    return 2 * Math.random() - 1;
}
array.sort( comparer );

EDIT Since some people are not satisfied with the solution, then here's more classical approach:

Array.prototype.shuffle = function() {
    var result = [];
    while( this.length ) {
        var index = Math.floor( this.length * Math.random() );
        result.push( this[ index ] );
        this.splice(index, 1);
    }
    return result;
};
freakish
  • 54,167
  • 9
  • 132
  • 169
  • seriously? Turning an `O(n)` operation into one that's at best `O(n log n)` – Alnitak Aug 20 '12 at 08:20
  • @Alnitak So? It is what OP wants and is the simpliest and fastest solution. – freakish Aug 20 '12 at 08:21
  • And sorting by an incorrect comparer? The comparison operation is assumed to be time-invariant, anti-commutative, transitive etc. – penartur Aug 20 '12 at 08:21
  • no, it is _not_ the fastest. And the whole point of the OP's question is to randomize the array to ensure that he _doesn't_ get a degenerate `O(n ^ 2)` sort. – Alnitak Aug 20 '12 at 08:22
  • @Alnitak Fastest in the sense of lines of codes. – freakish Aug 20 '12 at 08:25
  • 2
    BTW, it is written in ECMAScript v3 specification (paragraph 15.4.4.11) that, if the comparer is not consistent, the behaviour of `sort` is implementation-defined. That is, some ECMAScript implementations may simply throw an error when given such a function. – penartur Aug 20 '12 at 08:26
  • I doubt the result of this algorithm is uniformly distributed – amit Aug 20 '12 at 08:26
  • @amit It depends on the specific ECMAScript implementation. That is, some browsers may leave array as-is, some may sort it uniformly, some may sort it non-uniformly, and some may throw an error. – penartur Aug 20 '12 at 08:27
  • @freakish ah, you didn't mean _fastest_, you meant _laziest_! Now I understand... – Alnitak Aug 20 '12 at 08:28
  • @Alnitak What is the point of your comment? If you don't want to be lazy, then feel free to implement everything by yourself, starting with OS. – freakish Aug 20 '12 at 08:56
  • @penartur Can you show me well-known JavaScript implementation which will not work with the solution? If you can, then I stand corrected. If you can't, then this entire theoretical discussion is pointless. – freakish Aug 20 '12 at 08:57
  • http://www.codinghorror.com/blog/2007/12/the-danger-of-naivete.html – Alnitak Aug 20 '12 at 09:21
  • @freakish Even if there is no such an implementation now, it could appear in the future. Consider the theoretical example: IE11 will feature a rewritten JS engine, which will throw an error in this case. I guess this problem will be quite practical for the developer, unless their task is "write the code, pray it won't break, deliver, get payment, disappear; it is no longer your problem once you got your payment". – penartur Aug 20 '12 at 09:30
  • regardless, my original point still stands. For the OP to care whether his existing data sorts in `O(n log n)` vs `O(n ^ 2)` he must have enough points that going from an `O(n)` shuffle to an `O(n log n)` (best case) shuffle is a bad idea. – Alnitak Aug 20 '12 at 09:31
  • @penartur However it is **highly** unlikely that implementation of `.sort` will change. And if it does, then we'll be talking about backwards incompatibile version. Also it is impossible to throw error in `.sort` because this requires interpreter to know whether comparer is a metric (in mathematical sense) or not and this is algorithmically **impossible**. I agree that it is not the most beautiful solution, but I disagree that there is a problem with it. Anyway I've posted classical solution, so OP can choose on his own. – freakish Aug 20 '12 at 09:51
  • @Alnitak I have no idea what you are talking about. Sorting is `O(n logn)` and OP wants `O(n logn)` solution. Everything's fine. I don't know what OP thinks. I've written answer based on what is written. And I don't care about things which are not clearly included in question. – freakish Aug 20 '12 at 09:52
  • @freakish The algorithm specified by the OP is _average_ `O(n log n)` but in the worst case (if the data is already sorted) it degenerates to `O(n ^ 2)`. Hence the OP wants to shuffle the data to ensure it doesn't hit that worse case. – Alnitak Aug 20 '12 at 12:07
  • @freakish "*However it is highly unlikely that implementation of .sort will change*" - it is highly likely, as there is a lot of "new" implementations. "*And if it does, then we'll be talking about backwards incompatibile version*" - implementations do not need to be backwards-compatible (with what?), they need to be standards-compliant. – penartur Aug 21 '12 at 06:43
  • @freakish "*Also it is impossible to throw error in .sort because this requires interpreter to know whether comparer is a metric (in mathematical sense) or not and this is algorithmically impossible*" - `if (arr.length >= 2 && (comparer(arr[0], arr[1]) * comparer(arr[1], arr[0]) > 0)) { throw new Error("Your comparer is wrong"); }` – penartur Aug 21 '12 at 06:43
  • @penartur There are no new implementations. And there won't be because quicksort is optimal on a very big set of data. Algorithms which go below `O(n logn)` are faster then quicksort only for huge `n` and browser won't handle such big amount of data anyway. This argue is pointles. – freakish Aug 21 '12 at 07:06
  • @penartur This is not an algorithm. You cannot guarantee that if `comparer` satisfies this then `comparer` is a partial order ( there is a 50% chance that my function will satisfy this ). You can only guarantee that if it **does not** satisfy it, then it is not. Again, the argue is pointless. – freakish Aug 21 '12 at 07:09
  • Please move extended discussions to [chat]. – Tim Post Aug 21 '12 at 07:16
  • @freakish I didn't say anything about guarantee. The code I posted, being prepended to standards-compliant `Array.prototype.sort` body, will still make perfectly standards-compliant `Array.prototype.sort` implementation; but the code from your answer will fail about 50% of the time. This is clearly not what OP wants. – penartur Aug 21 '12 at 09:32
  • @penartur You are obviously wrong! If you go through any *divide and conquer* algorithm you'll notice that actually they do not require comparing function to be a partial order ( I don't know why you want to enforce this on my solution? ). My solution will work 100% of time. No one cares about what kind of function is passed as a comparer and none will ever enforce anything on these functions. – freakish Aug 22 '12 at 10:40
  • @Alnitak You are not wrong but you are not right either. :) This is because the worst case scenario assumes that we have natural order ( what does it mean that we start with sorted list if we have random order? ). Of course it still might happen that we hit `O(n^2)` but this is highly unlikely, because this means that my comparer evalutes exactly the same as natural order. And chances for that are very very low and are drastically getting closer to 0% the bigger initial list is. – freakish Aug 22 '12 at 10:41
  • @Alnitak Now, every element compared to any other element has 50% chance to be either lower or greater. Thus we can expect that two lists ( *lefts* and *rights* ) created in each step ( in quicksort ) will have more or less the same number of elements. Thus the expected complexity is still `O(n logn)` and it will get closer to `O(n logn)` the bigger initial list is. – freakish Aug 22 '12 at 10:42
  • The only question remaining is whether this solution generates each permutation with equal probability. I'm not sure of that one. – freakish Aug 22 '12 at 10:45
-1

You could extend the Array prototype with a shuffle function:

Array.prototype.shuffle = function() {
  var tmp, rand;
  for(var i =0; i < this.length; i++){
    rand = Math.floor(Math.random() * this.length);
    tmp = this[i]; 
    this[i] = this[rand]; 
    this[rand] = tmp;
  }
}

and then call shuffle on any array to do an in-place sorting:

var arr = [1,2,3,4];
arr.shuffle();
console.log(arr);
Florian
  • 3,366
  • 1
  • 29
  • 35
  • this is not a correct shuffling algorithm. Each element should be swapped with itself, or some later element in the array, and _not_ any arbitrary element of the array. See http://adrianquark.blogspot.co.uk/2008/09/how-to-shuffle-array-correctly.html – Alnitak Aug 20 '12 at 08:28