First random number after setSeed in Java always similar

谁说我不能喝 提交于 2019-11-30 07:51:31
Stephen C

The Random class is designed to be a low overhead source of pseudo-random numbers. But the consequence of the "low overhead" implementation is that the number stream has properties that are a long way off perfect ... from a statistical perspective. You have encountered one of the imperfections. Random is documented as being a Linear Congruential generator, and the properties of such generators are well known.

There are a variety of ways of dealing with this. For example, if you are careful you can hide some of the most obvious "poor" characteristics. (But you would be advised to run some statistical tests. You can't see non-randomness in the noise added to your second image, but it could still be there.)

Alternatively, if you want pseudo-random numbers that have guaranteed good statistical properties, then you should be using SecureRandom instead of Random. It has significantly higher overheads, but you can be assured that many "smart people" will have spent a lot of time on the design, testing and analysis of the algorithms.

Finally, it is relatively simple to create a subclass of Random that uses an alternative algorithm for generating the numbers; see link. The problem is that you have to select (or design) and implement an appropriate algorithm.


Calling this an "issue" is debatable. It is a well known and understood property of LCGs, and use of LCGs was a concious engineering choice. People want low overhead PRNGs, but low overhead PRNGs have poor properties. TANSTAAFL.

Certainly, this is not something that Oracle would contemplate changing in Random. Indeed, the reasons for not changing are stated clearly in the javadoc for the Random class.

"In order to guarantee this property, particular algorithms are specified for the class Random. Java implementations must use all the algorithms shown here for the class Random, for the sake of absolute portability of Java code."

Piotr Praszmo

This is known issue. Similar seed will generate similar few first values. Random wasn't really designed to be used this way. You are supposed to create instance with a good seed and then generate moderately sized sequence of "random" numbers.

Your current solution is ok - as long as it looks good and is fast enough. You can also consider using hashing/mixing functions which were designed to solve your problem (and then, optionally, using the output as seed). For example see: Parametric Random Function For 2D Noise Generation

Move your setSeed out of the loop. Java's PRNG is a linear congruential generator, so seeding it with sequential values is guaranteed to give results that are correlated across iterations of the loop.

ADDENDUM

I dashed that off before running out the door to a meeting, and now have time to illustrate what I was saying above.

I've written a little Ruby script which implements Schrage's portable prime modulus multiplicative linear congruential generator. I instantiate two copies of the LCG, both seeded with a value of 1. However, in each iteration of the output loop I reseed the second one based on the loop index. Here's the code:

# Implementation of a Linear Congruential Generator (LCG)
class LCG
  attr_reader :state
  M = (1 << 31) - 1    # Modulus = 2**31 - 1, which is prime

  # constructor requires setting a seed value to use as initial state
  def initialize(seed)
    reseed(seed)
  end

  # users can explicitly reset the seed.
  def reseed(seed)
    @state = seed.to_i
  end

  # Schrage's portable prime modulus multiplicative LCG
  def value
    @state = 16807 * @state % M
    # return the generated integer value AND its U(0,1) mapping as an array
    [@state, @state.to_f / M]
  end
end

if __FILE__ == $0
  # create two instances of LCG, both initially seeded with 1
  mylcg1 = LCG.new(1)
  mylcg2 = LCG.new(1)
  puts "   default progression     manual reseeding"
  10.times do |n|
    mylcg2.reseed(1 + n)  # explicitly reseed 2nd LCG based on loop index
    printf "%d %11d %f %11d %f\n", n, *mylcg1.value, *mylcg2.value
  end
end

and here's the output it produces:

   default progression     manual reseeding
0       16807 0.000008       16807 0.000008
1   282475249 0.131538       33614 0.000016
2  1622650073 0.755605       50421 0.000023
3   984943658 0.458650       67228 0.000031
4  1144108930 0.532767       84035 0.000039
5   470211272 0.218959      100842 0.000047
6   101027544 0.047045      117649 0.000055
7  1457850878 0.678865      134456 0.000063
8  1458777923 0.679296      151263 0.000070
9  2007237709 0.934693      168070 0.000078

The columns are iteration number followed by the underlying integer generated by the LCG and the result when scaled to the range (0,1). The left set of columns show the natural progression of the LCG when allowed to proceed on its own, while the right set show what happens when you reseed on each iteration.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!