0

I tried to use recursion for the problem at hand as follows,

int newlevelgen()
{
    int exampleno = Random.Range (1,4);
    if (exampleno != lastlevelno)
    {
        lastlevelno = exampleno;
        return exampleno;
    }
    else
    {
        newlevelgen();
    }
    return exampleno;
}

This is my code above, what I want to do is generate new number without repeating the previous one, but this simply does not work. Help!

Fattie
  • 27,874
  • 70
  • 431
  • 719
  • "simply does not work" is not helpful. What does not work? Is there an error? What line throws an error or does not work as expected? – Equalsk Feb 10 '16 at 11:09
  • Please clarify: Do you avoid repeating just the previous __one__ ( as per ypur text) or __any__ duplicates (as per your title)? – TaW Feb 10 '16 at 11:15
  • This must be the single must duplicated question in all of computing! To get "non repeating random values" **you just shuffle**. Coincidentally I just wrote a long explanation about it, since I have time on my hands .. http://stackoverflow.com/a/35228592/294884 – Fattie Feb 10 '16 at 12:11
  • it was all random like the if condition did not matter at all,and it did not,thanks will keep up such tips for future posting – Piyush Damania Feb 10 '16 at 12:24
  • 1
    Fact is your answer is easy to understand but bad design. When you have a chance to enter an infinite loop even the tiniest, you are in the wrong path because there is no such thing as "little is never" in computing. As I mentioned, your answer has 33% chances to enter a infinite loop. – Everts Feb 10 '16 at 12:33
  • 1
    Possible duplicate of [Random integer in a certain range excluding one number](http://stackoverflow.com/questions/34182699/random-integer-in-a-certain-range-excluding-one-number) – Fattie Feb 10 '16 at 12:34
  • @fafase No, the chance is only 33% per call but for an infinite loop it is 33% to the power of the number of recursive calls. Infinite loop -> 0.33 to the power infinity -> 0% chance. – Mark Feb 10 '16 at 12:35
  • Hi Mark. You don't seem to understand. ***THE ALGORITHM*** is an infinite loop. It ***DOES NOT*** have closure. Just consider different inputs. Silly stuff. It's an incredibly silly discussion because the answer is ***so well known*** and indeed so incredibly obvious. – Fattie Feb 10 '16 at 12:38
  • 1
    @Mark first round, I got 33% to get the same, so it jumps to recursion. I'm now on a new method call, I got 33% for a call. And so on. This is not a case where the probabilities add up or multiply. It is not a case where the current prob is influenced by the previous or the next. Each is independent, each is 33%. – Everts Feb 10 '16 at 12:42
  • 1
    @fafase Exactly, each _individual_ call is 33% but that does _not_ mean you have a 33% to enter an infinite loop because then you are _predicting_ how many times you will get the 33% chance result which is 33% to the power of the amount of times you are predicting that result to occur. And for infinite that will go to zero. But this is getting a bit off topic ;) – Mark Feb 10 '16 at 12:53
  • I feel this should not actually be closed (that has to be the first time I've ever typed that) since as Mark points out, the question is essentially asking "I couldn't get this to work *with recursion*..." Understanding the errors involved in that issue is an important question. (If the question was just the age-old "how do I get random non-repeating numbers", sure, close it.) – Fattie Feb 10 '16 at 14:25
  • 1
    "Exactly, each individual call is 33%" - when you talk about an *algorithm* being indeterminate, precisely what you are talking about *is the exit condition.* "But this is getting a bit off topic" au contraire, it's the single most important issue in thinking about recursive algorithms. (Or indeed, "algorithms" - i.e., is it determinate.) – Fattie Feb 10 '16 at 14:28

3 Answers3

0

Let me preface this by saying that the following is an answer to the problem with the original method that was posted. It is far from that best way to get the desired result but that's not the focus of this answer. Yes there is a chance that the recursion will go on for a few calls. 33% chance to need another recursive call, though that does not mean a 33% for an infinite loop as the likelihood of needing X recursive calls is 0.33^X so the likelihood of reaching 3 recursive calls is only 0.33^3 ~= 0.036. Still, a different method is advised. See Joe Blow's answer for example.

There's a problem with your recursion. You aren't using the new value that the recursive call to newlevelgen() would give you in case exampleno is the same as lastlevelno and always returning the (possibly duplicate) exampleno value. Change it to:

int newlevelgen()
{
    int exampleno = Random.Range (1,4);
    if (exampleno != lastlevelno)
    {
        lastlevelno = exampleno;
        return exampleno;
    }
    else
    {
        return newlevelgen();
    }
}
Mark
  • 1,360
  • 1
  • 8
  • 14
  • I would avoid this case of recursion since you only have three numbers, so 33% chances to go for another round on each iteration. – Everts Feb 10 '16 at 11:40
  • "Never, ever" is quite the bold statement. Recursion can be extremely useful when done right. The code above does _not_ give an infinite loop (provided `Random.Range` yields a different value eventually). That's not to say I would use this particular method myself, but it's exactly the answer to the question that was asked (i.e. why the code posted in the question doesn't work). – Mark Feb 10 '16 at 12:24
  • @Mark Recursion has a point where it has a point, here it does not. Recursion can be used on a collection where you removed items off the collection on each round and pass the new collection to the same method until the collection is empty (or any other conditions for that matters) and then you stop. And this is the big hit, you need a condition that does not fail 100% so your recursion can stop and leave all the stacked calls. Your condition can fail 33% (damn we're back with this again...) I'm out. – Everts Feb 10 '16 at 12:49
  • What's the likelihood of tossing a coin 4 times and getting heads 4 times in a row? Exactly. So what's the likelihood of getting the same number 4 times in a row? Not 33%. Nevertheless I've added a preface to state that I do not and did not think it's a good way to get the number, but it's the answer to the question that was asked. Anyway I'm out too. – Mark Feb 10 '16 at 13:26
  • 1
    Just as Mark says, this answer perfectly explains what is *wrong with the example code as given*. However, as Mark also points out (a) you'd never use recursion here and (b) this *algorithm* as such *is an infinite loop* - you cannot conclude an algorithm with an indeterminate decision. (interestingly, @Mark, some compilers even *pick up on this* - compilers amaze me these days, we all need new jobs.) – Fattie Feb 10 '16 at 14:07
0

The idea is that you get a value between a and b. If that value is greater of equal to previous value, then you increase that and return it. Other case, you return as is.

Think of it, it does work.

public int GetUniqueLevelInclusiveOrdinal(int a , int b, int previous){

    // given the ordinal numbers from a to b INCLUSIVE, 
    // so including a and b,
    // (*** NOTE, no random int calls in Unity or any system
    // work inclusively so take care ***)
    // choose one of the numbers from a to b inclusive
    // but do not choose "previous"

    // which top index to use with Random.Range which is exclusive?
    int top = b+1;

    // everyday algorithm for excluding one item from a random run
    int topExcludeOne = top-1;

    int value = Random.Range(a, topExcludeOne);
    if (value >= previous) return value+1;
    else return value;
}

This is an extremely well-known, basic, pattern in programming...

int result = UnityEngine.Random.Range(0,highest-1);
if (result>=exclude)
   return result+1;
else
   return result;

In Unity you must use extensions:

public static int RandomIndexButNotThis(this int highest, int exclude)
    {
    if (highest<2) Debug.Break();

    int result = UnityEngine.Random.Range(0,highest-1);
    if (result>=exclude)
       return result+1;
    else
       return result;
    }

To get a random index 0 to 99

Random.Range(0,100)

To get a random index 0 to 99, but excluding 61

100.RandomIndexButNotThis(61)

To get a random index 0 to 9

Random.Range(0,10)

To get a random index 0 to 9, but excluding 8

10.RandomIndexButNotThis(8)

If new to Unity, intro to extensions

Fattie
  • 27,874
  • 70
  • 431
  • 719
Everts
  • 10,408
  • 2
  • 34
  • 45
  • dude, all you do to get a random number from an array is add the length (obviously, modulo) and to get a random number *but not the same one* you just add the length minus one (again obviously modulo) – Fattie Feb 10 '16 at 12:17
  • (that's if, for some reason, you want to use an array. if you just want a random integer up to say 100, but not a certain single excluded number E, you just take a random number up to 100-1 and return `r>=E?r+1:r` ) – Fattie Feb 10 '16 at 12:21
  • I feel like this is kinda reducing the probability to get the last value. It can only be chosen if the previous choice was the value before it. So you can only get 100 if 99 was the previous choice and you get 99 this round again. Isn't it? – Everts Feb 10 '16 at 12:29
  • Oh ok I see. it increases if you have greater or equal. So yes you have the full panel. – Everts Feb 10 '16 at 12:36
  • mmm would have thought that min should be smaller than max, but actually not. – Everts Feb 10 '16 at 12:45
  • Coz I don't expect a new programmer to fully grasp the second part (ternary operator). The array thingy is more lengthy but somehow more "understandable". – Everts Feb 10 '16 at 12:58
  • I took the liberty of adding a comment explaining the call works **INCLUSIVE**. (That could cause considerable confusion to anyone using the algorithm in a typical EXCLUSIVE random call.) Feel free to revert etc – Fattie Mar 01 '16 at 15:56
0

I created utility to easely handle random without repetitions, flat distributed random, and weighted lists (universal property drawer included!). You can find it on GtiHub and use freely: https://github.com/khadzhynov/RandomUtils