Java random always returns the same number when I

2020-01-22 13:37发布

问题:

I require help with a random number generator I am creating. My code is as follows (inside a class called numbers):

public int random(int i){
    Random randnum = new Random();
    randnum.setSeed(123456789);
    return randnum.nextInt(i);
}

When I call this method from another class (in order to generate a random number), it always returns the same number. For example if I were to do:

System.out.println(numbers.random(10));
System.out.print(numbers.random(10));

it always prints the same number e.g. 5 5. What do I have to do so that it prints two different numbers e.g. 5 8

It is mandatory that I set the seed.

Thanks

回答1:

You need to share the Random() instance across the whole class:

public class Numbers {
    Random randnum;

    public Numbers() {
        randnum = new Random();
        randnum.setSeed(123456789);
    }

    public int random(int i){
        return randnum.nextInt(i);
    }
}


回答2:

If you always set the seed, you will always get the same answer. That is what setting the seed does.



回答3:

There are two issues causing what you see. The first is that the code sets a seed value for a Random instance. The second is that the instance method "random" instances a new Random object and then immediately sets its seed with the same seed every time. The combination of these two guarantees that, for the same value of i, the method "random" will always return the same value and it will always be the first in the sequence that the seed always generates.

Assuming setting the seed is mandatory, to get the next value in the sequence instead of the same first value of the sequence every time, the randnum instance of Random can't have its seed set every time just before its next method gets called. To fix that, move the randnum local variable instance of Random from the scope of the random instance method to the class scope. Second, set the seed only when random is assigned a Random instance or only to get same sequence of results from it to start over again. Class Random's setSeed(long seed) instance method can't execute in the class scope, so the constructor has to set it using the Random constructor with the long seed parameter. The following code shows the changes:

public class RandomDemo { // arbitrary example class name
    // lots of class related stuff may be here...

    // still inside the class scope...
    // private is a good idea unless an external method needs to change it
    private Random randnum = new Random(123456789L);
    // the seed guarantees it will always produce the same sequence
    // of pseudo-random values when the next methods get called
    // for unpredicable sequences, use the following constructor instead:
    // private Random randnum = new Random();

    // lots of code may be here...

    // publicly exposed instance method for getting random number
    // from a sequence determined by seed 123456789L
    // in the range from 0 through i-1
    public int randnum(int i) {
        // don't set the seed in here, or randnum will return the exact same integer
        // for the same value of i on every method call
        // nextInt(i) will give the next value from randnum conforming to range i
        return randnum.nextInt(i);
    } // end randnum

    // lots of more code may be here...

} // end class RandDemo

The above will give you an exact solution to your exact problem, as stated. However, using a mandatory seed seems unusual, given what it does.

If this is for a class project or software testing where the sequence has to be predictable and repeatable, setting the seed to a fixed value makes sense. Otherwise, question the validity of setting the seed to some predetermined value. The following explains more about Random, seeds for Random and why there is a provision for supplying a seed.

Random has two constructors:

Random()

and

Random(long seed)

and an instance method

setSeed(long seed)

that all affect the sequence of numbers obtained from a Random instance. The instance method,

setSeed(long seed)

sets the Random object to the same state it would have been in had it been just instanced with the same seed as the constructor argument. Only the low-order 48 bits of a seed value get used.

If a Random object is instanced without a seed, the seed will be the same as the system time in milliseconds. This ensures that, unless two Random objects are instanced in the same millisecond, they will produce different pseudo-random sequences. Only the low order 48 bits of the seed value gets used. This causes unpredictable pseudo-random sequences. It is not necessary and wasteful of computing resources to get a new instance of Random every time one calls a next method.

Random's seed parameters are provided so that one may instance a Random object that produces a sequence that is repeatable. For a given seed, the sequence of values in next methods are guaranteed to be the same sequence whenever that seed is used. This is useful for testing software that is going to use pseudo-random sequences where results have to be predicable and repeatable. It is not useful for creating different unpredictable pseudo-random sequences in operation.

The statement "it is mandatory that I set the seed" negates any unpredictablity of the Random object's pseudo-random sequences. Is this for a class project or software test where results have to be the same for the same inputs to the program?



回答4:

Set the seed once on startup, rather than every time you want a new random number.



回答5:

What you are using is not a Random Number Generator, it's a Pseudo-Random Number Generator. PRNG's generate sequences of pseudo-random numbers, the seed selects a starting point in a sequence (a PRNG may generate one or several sequences).



回答6:

Do you necessarily need to create the new Random() inside your random(int i) method? If you are OBLIGED to do it that way, you could use, you could set the seed to the current time, although that is not fail proof, because you could call your numbers.random(10) so fast after the other that it would end up being the same seed. You could try maybe using nanoSeconds (System.nanoTime() I think? And if setSeed only accepts int, multiply it I guess).

What I would suggest however, if you are allowed to do so, is to declare your Random outside your method. If you instanciate your random variable in, say, your number class constructor, you could set any seed and any time you call your method it would give you a new number. (They will be the same sets of numbers every time you restart your application if you use a constant seed however, you could use time as your seed in this case too).

Finally, the last problem could be if you declare several number classes at the same time. They will all have the same random seed and give you the same set of random numbers. If this happens, you can make a static Random in your main class, and call it in your numbers class. This will couple those two classes though, but it would work. Another option would be to send an incrementing value to your number class constructor, for each number you instanciate, and use the value you pass as the seed.

The second option should be good for you though, if you are allowed to do it that way.



回答7:

Usually, Random is not truly random but pseudorandom. It means it takes a given seed and uses it to generate a sequence of numbers that looks like random (but is enterely predictable and it repeats if you put the same seed).

If you don't put seed, then the first seed will be taken from a variable source (usually the system time).

Usually, a value with seed will be used in order to have it repeat the exact values (for example, for testing). Use Random without seed instead.



标签: java random