繁体   English   中英

如何确定地从X / Y坐标生成伪随机模式?

[英]How can I produce a pseudorandom pattern from X/Y coordinates deterministically?

我正在编写一个着色器,有时会在2D地图上使一个点闪烁。 (“闪闪发光”只是一个较亮的像素。)我希望闪闪发光的块随机且均匀地分布在(无限)平面上,但是我希望闪闪发光是基于X和Y坐标的确定性。 我尝试从坐标创建种子,并从该种子创建Java Random ,但是到目前为止,我的尝试已产生了可识别的模式。 该函数将被频繁调用(数百万次),因此性能至关重要。

我首先尝试模仿我的hashCode()实现,该实现使用质数乘法器来避免冲突。 这导致整个地图上出现可见的裂口,其中一系列点共享同一种子。

然后,我尝试通过连接坐标来创建种子,如下所示:

long seed = ((long) x << 32) | (long) y;
Random rand = new Random(seed);

尽管模式并不那么明显,但这似乎也会导致模式化数据。 选定的坐标以线条显示,根本没有均匀分布。

我避免使用MD5或其他加密哈希算法,因为我担心会影响性能。

以下是用于以伪随机但确定的方式混合位的非常有效的函数:

public static final long xorShift64(long a) {
    a ^= (a << 21);
    a ^= (a >>> 35);
    a ^= (a << 4);
    return a;
}

因此,如果您希望从x和y坐标获得伪随机长结果,则可以执行以下操作:

    long mix = xorShift64(x) + Long.rotateLeft(xorShift64(y),32) + 0xCAFEBABE;
    long result = xorShift64(mix);

我以前在图形中成功使用了这种方法,效果很好! 随机数的质量与java.util.Random差不多,但是速度要快得多。

java.util.Random实现的线性同余生成器的优点是,对于任何选择的SEED都是可重复的。 鉴于这些声明,

private static final int SEED = 42;
private static final int N = 128;
private static final int MAX_X = 1024;
private static final int MAX_Y = 1024;
private final Random rnd = new Random(SEED);
private final List<SparklePoint> list = new ArrayList<SparklePoint>(N);

您可以初始化矩形(0, 0, MAX_X, MAX_Y)N随机选择的点的(可重复)列表,如下所示:

public void init(int seed) {
    for (int i = 0; i < N; i++) {
        int x = rnd.nextInt(MAX_X);
        int y = rnd.nextInt(MAX_Y);
        list.add(new SparklePoint(x, y));
    }
}

为每个点提供一个Timer ,该Timer的周期是从相同的序列中选择的,这可能很方便:

private class SparklePoint implements ActionListener {

    private static final int MAX_DELAY = 1000;
    private final Point p;
    private final Timer t;
    private boolean bright;

    public SparklePoint(int x, int y) {
        p = new Point(x, y);
        t = new Timer(rnd.nextInt(MAX_DELAY), this);
        t.setRepeats(false);
        t.start();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        t.stop();
        if (bright) {
            // darken p
        } else {
            // brighten p
        }
        bright = !bright;
        t.setDelay(rnd.nextInt(MAX_DELAY));
        t.start();
    }
}

这是我所做的工作(可以产生所需的效果),但绝对不是完美的。

MessageDigest md5;
try {
    md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
    return null;
}
md5.update(new byte[] {
    (byte)(x >>> 24),
    (byte)(x >>> 16),
    (byte)(x >>> 8),
    (byte)x,
    (byte)(z >>> 24),
    (byte)(z >>> 16),
    (byte)(z >>> 8),
    (byte)z
}, 0, 8);
byte[] digest = md5.digest();
long seed = digest[0] + (digest[1] << 8) + (digest[2] << 16) + (digest[3] << 24) + (digest[4] << 32) + (digest[5] << 40) + (digest[6] << 48) + (digest[7] << 56);
Random random = new Random(seed);

除了特别冗长之外, Random的使用可能过多,因为我只两次调用了nextInt() 这对于生成特定范围内的值很有用,但是无论如何我应该能够通过模运算来实现。

我喜欢MD5是一种易于理解的算法,并且密码安全性对于此应用程序并不重要。 不过,我绝对希望更快(并且更不会混乱)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM