【问题标题】:Java - is there a way to generate random, but deterministic signal?Java - 有没有办法生成随机但确定性的信号?
【发布时间】:2020-09-08 10:54:48
【问题描述】:

我正在用 Java 编写一个信号生成器应用程序,其中一个功能是生成一个具有高斯分布的噪声信号。我当前返回此类值的实现如下:

import java.util.Random;

public class FormulaGaussianDistributionNoise extends Formula {

    private Double stdEv;
    private Random r;

    public FormulaGaussianDistributionNoise(Double stdEv) {
        this.stdEv = stdEv;
        this.r = new Random();
    }

    @Override
    public Double value(Double x) {
        return stdEv*r.nextGaussian();
    }
}

当我生成 ie 时。具有 100Hz 采样的 10 秒信号我得到了漂亮的高斯噪声信号,如下所示: Great gaussian signal

但是问题是我希望我的信号一旦生成就成为确定性的。可能很多人都注意到了,如果使用相同的参数调用两次,我准备的类将返回两个不同的值。我想实现我的方法value(Double x)FormulaGaussianDistributionNoise 使用相同参数调用的对象总是返回相同的值。

到目前为止,我尝试在 value(Double x) 的每次调用中使用 Random 类的方法 setSeed(long x)。然后我的班级看起来像这样:

public class FormulaGaussianDistributionNoise extends Formula {

    private Double stdEv;
    private Random r;
    private long seed;

    public FormulaGaussianDistributionNoise(Double stdEv) {
        this.stdEv = stdEv;
        this.seed = System.currentTimeMillis();
        this.r = new Random(seed);
    }

    @Override
    public Double value(Double x) {
        //Generate new seed based on the double value 
        long newSeed = Double.doubleToRawLongBits(x);
        r.setSeed(this.seed ^ newSeed);
        return stdEv*r.nextGaussian();
    }
}

this.seed 类的属性用于使该类的对象彼此不同。在value(x) 调用中,我将Random 对象的新种子设置为this,seedx 的异或位。因此,如果使用相同的x 调用,它总是为r 对象设置相同的种子,因此nextGaussian() 总是返回相同的值。问题是实际上这些值经常重复并且信号看起来不再像高斯了: Great NO-gaussian signal

我对此进行了更深入的研究,setSeed(long seed)Random 类的实现如下所示:

    /**
     * Sets the seed of this random number generator using a single
     * {@code long} seed. The general contract of {@code setSeed} is
     * that it alters the state of this random number generator object
     * so as to be in exactly the same state as if it had just been
     * created with the argument {@code seed} as a seed. The method
     * {@code setSeed} is implemented by class {@code Random} by
     * atomically updating the seed to
     *  <pre>{@code (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1)}</pre>
     * and clearing the {@code haveNextNextGaussian} flag used by {@link
     * #nextGaussian}.
     *
     * <p>The implementation of {@code setSeed} by class {@code Random}
     * happens to use only 48 bits of the given seed. In general, however,
     * an overriding method may use all 64 bits of the {@code long}
     * argument as a seed value.
     *
     * @param seed the initial seed
     */
    synchronized public void setSeed(long seed) {
        this.seed.set(initialScramble(seed));
        haveNextNextGaussian = false;
    }

    private static long initialScramble(long seed) {
        return (seed ^ multiplier) & mask;
    }

    private static final long multiplier = 0x5DEECE66DL;
    private static final long mask = (1L << 48) - 1;

如您所见,setSeed(long seed) 方法将种子设置为一些 initialScramble(seed),它实际上只使用了我的 64 位种子的 48 位。当我这样炒我的种子时,实际上我经常得到相同的种子:

    this.seed      x                newSeed   this.seed^newSeed [y] initialScramble(y)
1590082351125   0.0                       0           1590082351125      1614482495096  
1590082351125   0.25    4598175219545276416     4598176809627627541      1614482495096  
1590082351125   0.5     4602678819172646912     4602680409254998037      1614482495096  
1590082351125   0.75    4604930618986332160     4604932209068683285      1614482495096  
1590082351125   1.0     4607182418800017408     4607184008882368533      1614482495096  
1590082351125   1.25    4608308318706860032     4608309908789211157      1614482495096  
1590082351125   1.5     4609434218613702656     4609435808696053781      1614482495096  
1590082351125   1.75    4610560118520545280     4610561708602896405      1614482495096  
1590082351125   2.0     4611686018427387904     4611687608509739029      1614482495096  
1590082351125   2.25    4612248968380809216     4612250558463160341      1614482495096  
1590082351125   2.5     4612811918334230528     4612813508416581653      1614482495096  
1590082351125   2.75    4613374868287651840     4613376458370002965      1614482495096  
1590082351125   3.0     4613937818241073152     4613939408323424277      1614482495096  
1590082351125   3.25    4614500768194494464     4614502358276845589      1614482495096  
1590082351125   3.5     4615063718147915776     4615065308230266901      1614482495096  
1590082351125   3.75    4615626668101337088     4615628258183688213      1614482495096  
1590082351125   4.0     4616189618054758400     4616191208137109525      1614482495096  
1590082351125   4.25    4616471093031469056     4616472683113820181      1614482495096  
1590082351125   4.5     4616752568008179712     4616754158090530837      1614482495096  
1590082351125   4.75    4617034042984890368     4617035633067241493      1614482495096  
1590082351125   5.0     4617315517961601024     4617317108043952149      1614482495096  
1590082351125   5.25    4617596992938311680     4617598583020662805      1614482495096  
1590082351125   5.5     4617878467915022336     4617880057997373461      1614482495096  
1590082351125   5.75    4618159942891732992     4618161532974084117      1614482495096  
1590082351125   6.0     4618441417868443648     4618443007950794773      1614482495096  
1590082351125   6.25    4618722892845154304     4618724482927505429      1614482495096  
1590082351125   6.5     4619004367821864960     4619005957904216085      1614482495096  
1590082351125   6.75    4619285842798575616     4619287432880926741      1614482495096  
1590082351125   7.0     4619567317775286272     4619568907857637397      1614482495096  
1590082351125   7.25    4619848792751996928     4619850382834348053      1614482495096  
1590082351125   7.5     4620130267728707584     4620131857811058709      1614482495096  
1590082351125   7.75    4620411742705418240     4620413332787769365      1614482495096  
1590082351125   8.0     4620693217682128896     4620694807764480021      1614482495096  
1590082351125   8.5     4620974692658839552     4620976282741190677      1614482495096  
1590082351125   9.0     4621256167635550208     4621257757717901333      1614482495096  
1590082351125   9.5     4621537642612260864     4621539232694611989      1614482495096

但是覆盖 Random 类以摆脱 initialScramble 也没有帮助。我发现Random.next() 方法也只使用了 48 位种子,所以这可能是问题所在。

   /**
     * Generates the next pseudorandom number. Subclasses should
     * override this, as this is used by all other methods.
     *
     * <p>The general contract of {@code next} is that it returns an
     * {@code int} value and if the argument {@code bits} is between
     * {@code 1} and {@code 32} (inclusive), then that many low-order
     * bits of the returned value will be (approximately) independently
     * chosen bit values, each of which is (approximately) equally
     * likely to be {@code 0} or {@code 1}. The method {@code next} is
     * implemented by class {@code Random} by atomically updating the seed to
     *  <pre>{@code (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)}</pre>
     * and returning
     *  <pre>{@code (int)(seed >>> (48 - bits))}.</pre>
     *
     * This is a linear congruential pseudorandom number generator, as
     * defined by D. H. Lehmer and described by Donald E. Knuth in
     * <i>The Art of Computer Programming,</i> Volume 3:
     * <i>Seminumerical Algorithms</i>, section 3.2.1.
     *
     * @param  bits random bits
     * @return the next pseudorandom value from this random number
     *         generator's sequence
     * @since  1.1
     */
    protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) & mask;
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }

    private static final long multiplier = 0x5DEECE66DL;
    private static final long addend = 0xBL;
    private static final long mask = (1L << 48) - 1;

我的问题是:是否有使用 64 位种子的随机数生成器类来生成下一个值?我找不到任何东西。也许您有不同的解决方案来使我的value(Double x) 具有确定性?

【问题讨论】:

  • 预生成整个系列或缓存值。
  • @Olivier 您的解决方案有两个问题:首先,这会消耗大量内存,因为我通常会生成比仅提供的 1k 更多的值。第二:我也在序列化这个类,你提出的方式可以在每次反序列化后再次生成不同的值。
  • 你不能只保存种子吗?有了它,您将能够重新生成相同的系列。
  • 仅假设完全相同的采样 - 如果我以不同的频率或起点开始采样,我可以获得相同 x 值的不同值。

标签: java random signals noise seed


【解决方案1】:

我通过使用 java.security 包中的 SecureRandom 类达到了预期的效果。 这篇文章的回答帮助了我:How to generate all possible 64 bit random values in java?

工作实施:

import java.security.SecureRandom;

public class FormulaGaussianDistributionNoise extends Formula {

    private Double stdEv;
    private long basicSeed;

    public FormulaGaussianDistributionNoise(Double stdEv) {
        this.stdEv = stdEv;
        this.basicSeed = System.currentTimeMillis();
    }

    @Override
    public Double value(Double x) {
        SecureRandom r = new SecureRandom();
        long newSeed = Double.doubleToRawLongBits(x);
        r.setSeed(this.basicSeed ^ newSeed);

        return stdEv*r.nextGaussian();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-20
    • 1970-01-01
    • 2011-07-28
    • 1970-01-01
    • 2020-02-15
    • 1970-01-01
    相关资源
    最近更新 更多