如何确定地将连续整数映射为均匀分布的双精度

时间:2018-12-10 14:50:09

标签: java random hash

我正在接收一个大致连续的整数序列(来自数据库密钥),并且我想以确定性的方式将每个整数映射到0和1之间的一个双精度数,以便所有双精度数的结果集为(接近)均匀分布。理想情况下,即使只接收到少量整数,也应该如此。

映射必须是确定性的,因为对于每个整数(在多个不同的机器上),它可能会发生多次,并且每次映射都将产生相同的两倍。

例如函数mapToDouble如下所示?

final int start = 100000;
final int size = 2000;
final double[] results = new double[size];

for (int i = 0; i < size; i++) {
    results[i] = mapToDouble(i + start)
}

// results is uniformly distributed

到目前为止,我能想到的最好方法是:

double mapToDouble(final int i) {
        final String s = new StringBuilder().append(i).append(".0").reverse().toString();
        return new Double(s);
    }

对于样本量相对较小的最高有效位大约均匀分布,但对于较低有效位可以偏斜。

1 个答案:

答案 0 :(得分:2)

当然,您可以使用Linear Congruential Generator进行映射。基本上,具有满足Hull–Dobell定理的适当参数的LCG会将[0 ... 2 64 )范围内的任何整数唯一地映射到[0 ... 2 64 )范围,可以这么说。双打不是唯一的,它们在[0 ... 1)范围内不够。

Java伪代码(很抱歉,Java很久以前使用过,此处假设Java 8带有Long

static final long a = Long.parseUnsignedLong("2862933555777941757"); // values taken from https://nuclear.llnl.gov/CNP/rng/rngman/node4.html
static final long c = Long.parseUnsignedLong("3037000493");

double mapToDouble(final int i) {
    long seed = (long)i;
    long mapl = a * seed + c; // should do wraparound automatically, or use Long.remainderUnsigned
    double x  = (x >>> 11) * 0x1.0p-53; // see http://xoshiro.di.unimi.it/
    return x;
}

您可以尝试使用Lehmer RNG和质数253-111映射到唯一双精度数,这几乎可以保证统一的53位尾数。但是,模数计算必须明确进行。