1

For some reason the following code doesn't work.

var a1 = Math.floor(Math.random()*4+1); 

//Answer2
for(a2 = 0; a2 != a1 && a2 != 0; a2 = Math.floor(Math.random()*4+1)){
    alert(a2);
}

I'm trying to get "a2" to be a int from 1-4 but not equivalent to "a1".

What's wrong? Thanks in advance!!!

EDIT:

Thank you all for your help! Here's my final result:

var a1, a2;
a1 = Math.floor(Math.random()*4+1); 
a2 = a1;

while(a2 == a1){
        a2 = Math.floor(Math.random() * 3 + 1);
        alert(a2);
}
Stephen
  • 576
  • 8
  • 19
  • Any reason to prefer `Math.floor(x + 1)` over `Math.ceil(x)`? The only reason I could think of is that perhaps `Math.random()` return results in `[0,1)` – wim Sep 21 '12 at 04:03
  • @wim: Indeed, that's what `Math.random()` returns. Slulego: Shouldn't `alert(a2)` be outside the loop? – Ry- Sep 22 '12 at 14:30
  • I used alert(a2) just to keep track of whats going on ... (I actually started using console.log instead) – Stephen Sep 23 '12 at 22:40

6 Answers6

5

How about a while loop instead? Initialise a2 = a1 and then:

while(a2 == a1) {
    a2 = Math.floor(Math.random() * 4 + 1);
}
wim
  • 338,267
  • 99
  • 616
  • 750
  • In the existing code the 0 check is necessary at is the initialized value for a2. Using the for structure, a2 could/should be initialized using the same call to random to prevent the first pass through the loop to be a guaranteed failure. – Matt Whipple Sep 21 '12 at 02:19
  • 3
    How about a `do` loop? Don't initialize `a2 = a1` and then do the same thing: `do { a2 = Math.floor(Math.random() * 4 + 1); } while(a2 === a1);` – Ry- Sep 21 '12 at 02:36
  • Yes, that is a nicer suggestion .. coming from python background I did not know about do loops in javascript – wim Sep 21 '12 at 02:58
4

The logic of your test is inverted. For loops continue to execute while the test is true, not if it fails. Rather than: a2 != a1 && a2 != 0 you should have (a2 == a1) || (a2 == 0). Though also keep in mind that the alert will also only be executed in that case when a2 is an invalid state, though an alert following the for should be valid.

Or if you're looking for the fun math-y way to do it using modular arithmetic (no retries necessary):

a2 = (Math.floor(Math.random() * 3 + (a1 + 1)) % 4) || 4
Ry-
  • 218,210
  • 55
  • 464
  • 476
Matt Whipple
  • 7,034
  • 1
  • 23
  • 34
  • 1
    +1 for the "math-y way". It's a scary-looking expression until you see what it's doing: counting circularly, randomly pick a number from `a1 + 1` up to, but not including `a1`; finally, turn 0 into 4, since the set doesn't include 0. – Ted Hopp Sep 21 '12 at 03:30
2

Try with this function. It'll give you an array with numbers from 1 to max in random order. Then you can use it to assign a value to a variable and it'll never repeat:

Edit: I found a purely functional way to do it:

function randomRange(min, max) {
  return (new Array(++max-min))
    .join('.').split('.')
    .map(function(v,i){ return min+i })
    .sort(function(){ return 0|Math.random()*max });
}

console.log(rand(1,4)); //-> [4,2,1,3] random array from 1 to 4

var arr = rand(1,4);
var a = arr[0];
var b = arr[1];
...

Edit 2: Seems like the above has problems with some browsers, check out Random but just in Chrome for a better solution.

Demo: http://jsbin.com/ohohum/1/edit (run multiple times to see the randomness)

Community
  • 1
  • 1
elclanrs
  • 92,861
  • 21
  • 134
  • 171
  • -1 because dynamic regular expressions are a terrible way to test if something is in a string, and regular expressions are completely unnecessary here, and so are strings. – Ry- Sep 21 '12 at 02:34
1

Just add one if the number is greater than or equal to the one you want to exclude:

var a2 = Math.floor(Math.random() * 3 + 1);

if(a2 >= a1) {
    a2++;
}
Ry-
  • 218,210
  • 55
  • 464
  • 476
  • Ok but If it's false I need it to try again. Sorry I forgot to mention that. – Stephen Sep 21 '12 at 01:48
  • @Slulego: What do you mean "if it's false"? – Ry- Sep 21 '12 at 02:28
  • Also, why the downvote? This is waaay more efficient than a loop. – Ry- Sep 21 '12 at 02:30
  • I'm not the downvoter, but it's not immediately clear to me what the code does and whether or not it skews the distribution of a2 (think Bayes' law) – wim Sep 21 '12 at 02:56
  • 1
    @wim: It doesn't skew anything. This is how to *not* skew things :) – Ry- Sep 21 '12 at 02:57
  • Yes, I can see you are correct, but it's a clever idea and took me some pondering to figure out _how_ it works. In my opinion code clarity usually trumps being "waaay more efficient", especially if efficiency has not even been established as a requirement. – wim Sep 21 '12 at 03:04
  • @wim: I find it really, really, really, really, really, really clear personally, but if you want, I can add some comments. – Ry- Sep 21 '12 at 03:08
  • +1 - A nice solution to this specific problem. @wim - To me, a generate-and-test loop is not as clear as this; clarity is in what you are trained to see. – Ted Hopp Sep 21 '12 at 03:27
  • I congratulate you on being a really, really, really, really, really, really clever guy. was just trying to explain why you may have collected a couple of downvotes here, no thanks for your snarky comment on editing my post. – wim Sep 21 '12 at 03:37
1

You are choosing a new a2 value when a2 is not the same as a1 or zero. This is backwards - you set a2 to zero before the loop, so it never executes.

You need to loop while a2 is either zero, or equal to a1:

var a1 = Math.floor(Math.random()*4+1); 

//Answer2
for(a2 = 0; a2 == a1 || a2 == 0; a2 = Math.floor(Math.random()*4+1)){
    // nothing here
}   
alert(a2);
Blorgbeard
  • 101,031
  • 48
  • 228
  • 272
0

There are two standard ways of doing this kind of thing. I'm considering a slightly more general framework though

Sample from a filtered list

  1. Create the array of base values to select from

    var values = [1,2,3,4]
    
  2. Remove the values you're not interested in

    values = Array.filter( values, function(x) { return x!=a1; } )
    
  3. Select a value at random from the list

    var a2 = values[Math.floor(Math.random()*values.length)];
    

Use rejection sampling

  1. Create the array of values to select

     var values = [1,2,3,4]
    
  2. Select a value at random and retry until it succeeds

     var a2 =  values[Math.floor(Math.random()*values.length)];
     while( a2==a1 )
     {
       a2 = values[Math.floor(Math.random()*values.length)];
     }
    

Comparison

If we consider n to be the length of the initial list and m to be the number of elements removed, then the filtered version does n comparisons to build the filtered list of length n-m, but only one random number generation. In comparison the rejection method does on average n/(n-m) comparisons and random number generations.

Michael Anderson
  • 70,661
  • 7
  • 134
  • 187