为javascript编写一个真正包容的随机方法

时间:2010-01-27 00:20:52

标签: javascript math random

Javascript的MATH对象有一个随机方法,从set [0,1)0返回,包括1,exclusive。有没有办法返回包含1的真正随机方法。

e.g。

var rand = MATH.random()*2;

if(rand > 1)
{
   rand = MATH.floor(rand);
}

return rand; 

虽然这总是从集合[0,1]返回一个数字,但它并不是真正随机的。

10 个答案:

答案 0 :(得分:13)

这将返回[0,1]包含:

if(MATH.random() == 0)
    return 1;
else
    return MATH.random();

说明:如果第一次调用random()返回0,则返回1.否则,再次调用random,这将是[0,1)。因此,它将返回[0,1]全包。

答案 1 :(得分:8)

它是否包含边界应该无关紧要;事实上,严格来说,你要做的事情并没有多大意义。请记住,在连续概率分布下,获得特定值的概率无论如何都是无穷小的,所以从数学上讲,你永远不会看到1的确切值。

当然,在计算机世界中,RNG的分布并不是真正连续的,所以你“可能”会遇到一个特定的价值(无论这意味着什么),但事实上你是依赖关于如何存储实数的限制提示您解决您尝试解决的任何问题的问题。

答案 2 :(得分:6)

Math.random函数返回0到1之间的随机数,其中0表示包含,1表示独占。这意味着在一个区间内将随机数正确分配为整数的唯一方法是使用独占上限。

要指定包含上限,只需添加一个上限即可使其在计算中排除。这将在7到12之间正确分配随机数,包括:

var min = 7;
var max = 12;
var rnd = min + Math.floor(Math.random() * (max - min + 1));

答案 3 :(得分:3)

您希望它包含1?

return 1 - Math.random();

但是,我认为这是暗示其他问题的问题之一。为什么需要包括1?可能有更好的方法。

答案 4 :(得分:2)

从我在Chrome浏览器中的JavaScript控制台中可以看到,Math.random()生成一个从0到0.9999999999999999的数字。考虑到这一点,您可以通过添加修饰符来获得所需内容。例如,这是一个函数,它将为您提供0到1之间的准随机浮点数,其中包含1:

function randFloat() {
  // Assume random() returns 0 up to 0.9999999999999999
  return Math.random()*(1+2.5e-16);
}

您可以通过输入0.9999999999999999*(1+2.5e-16)在控制台中尝试此操作 - 它将完全返回1.您可以进一步使用此函数返回0到1024(包括)之间的浮点数:

function randFloat(nMax) {
  // Assume random() returns 0 up to 0.9999999999999999
  // nMax should be a float in the range 1-1024
  var nMod;
  if (nMax<4) nMod = 2.5e-16;
  else if (nMax<16) nMod = 1e-15;
  else if (nMax<32) nMod = 3.5e-15;
  else if (nMax<128) nMod = 1e-14;
  else if (nMax<512) nMod = 3.5e-14;
  else if (nMax<1024) nMod = 1e-13;
  return Math.random()*(nMax+nMod);
}

在某个地方可能有更高效的算法。

答案 5 :(得分:1)

由于再次提出这个问题,而我在这里没有读到这种方法,我会再添加一个答案。

IMO是你能做的最好的事情,没有太多麻烦:

排斥的:

//simply ignore the 0
for(var rand=0; !rand;rand = Math.random());

//or simpler:
var rand = Math.random() || Math.random();
//because the probability for two consecutive `0` is pretty much non existant.

这甚至不会引入错误,因为我们只是排除了返回0的可能性,0到1之间的每个其他值具有相同的概率

包括:

var rand = Math.random() * 2;
if(rand > 1) rand-=1;
//so the 0 shares it's probability with the 1.

只是要明确&#34;错误&#34;是:

  • 01的概率
    1 / Math.pow(2, 54)或约5.55e-17
  • 01之间任何其他值的概率
    1 / Math.pow(2, 53)或约11.1e-17

并且整个随机函数将是:

function random(min, max, inclusive){
    var r = Math.random();
    if(inclusive)
        r = r>0.5? 2*r-1: 2*r;
    else 
        while(!r) r = Math.random();

    return r * (max - min) + min;
}

编辑:我不确定我是否犯了错误,但如果我在零和一位上添加另一位,则不应该将概率固定在包容性方法上,因此重复他们的概率:

var rand = Math.random() * 4;
rand = (rand % 1) || (rand & 1);

答案 6 :(得分:0)

附录:

查看Oracle JDK 7发布中包含的java.util.Random源代码(“Copyright(c)1995,2010,Oracle和/或其附属公司。保留所有权利.ORACLE所有权/机密。使用受许可条款约束“)显示这个简单的代码:

class Random {

    public float nextFloat() {
        return next(24) / ((float)(1 << 24));
    }

    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));
    }
}

因此,nextFloat()

  1. 取0到2 ^ 24-1之间的“随机整数值”(或者更确切地说,将随机24位位模式解释为整数值),
  2. 将其转换为float(在Java中,“float”被强制为IEEE 724 32位浮点,它可以表示最多2 ^ 24而不会丢失精度,因此这将是0之间的值和1.6777215E7)
  3. 然后将它除以2 ^ 24的浮点表示,再次只能表示为1.6777216E7,不会丢失精度。当被迫浮动时,2 ^ 24 + 1 = 16777217将下降到1.6777216E7。在代码中,这应该是一个常量。嘿太阳,周期不会长在树上!!
  4. 除法导致浮点数为[0.0 .. 0.99999994] (正确的除法结果将在0.999999940395355224609375左右),我认为,所有可能的IEEE 724浮点值介于'之间可能的”。
  5. 另请参阅IEEE floating pointFloating-Point Arithmetic on the JVM

    Javadoc对`next()的评论是:

    /**
     * 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
     */
    

    nextFloat()的Javadoc评论是:

    /**
     * Returns the next pseudorandom, uniformly distributed {@code float}
     * value between {@code 0.0} and {@code 1.0} from this random
     * number generator's sequence.
     *
     * <p>The general contract of {@code nextFloat} is that one
     * {@code float} value, chosen (approximately) uniformly from the
     * range {@code 0.0f} (inclusive) to {@code 1.0f} (exclusive), is
     * pseudorandomly generated and returned. All 2<font
     * size="-1"><sup>24</sup></font> possible {@code float} values
     * of the form <i>m&nbsp;x&nbsp</i>2<font
     * size="-1"><sup>-24</sup></font>, where <i>m</i> is a positive
     * integer less than 2<font size="-1"><sup>24</sup> </font>, are
     * produced with (approximately) equal probability.
     *
     * <p>The method {@code nextFloat} is implemented by class {@code Random}
     * as if by:
     *  <pre> {@code
     * public float nextFloat() {
     *   return next(24) / ((float)(1 << 24));
     * }}</pre>
     *
     * <p>The hedge "approximately" is used in the foregoing description only
     * because the next method is only approximately an unbiased source of
     * independently chosen bits. If it were a perfect source of randomly
     * chosen bits, then the algorithm shown would choose {@code float}
     * values from the stated range with perfect uniformity.<p>
     * [In early versions of Java, the result was incorrectly calculated as:
     *  <pre> {@code
     *   return next(30) / ((float)(1 << 30));}</pre>
     * This might seem to be equivalent, if not better, but in fact it
     * introduced a slight nonuniformity because of the bias in the rounding
     * of floating-point numbers: it was slightly more likely that the
     * low-order bit of the significand would be 0 than that it would be 1.]
     *
     * @return the next pseudorandom, uniformly distributed {@code float}
     *         value between {@code 0.0} and {@code 1.0} from this
     *         random number generator's sequence
     */
    

答案 7 :(得分:0)

这应该可以正常工作。

function random_inclusive () {
    while (true) {
        var value = Math.random() + (Math.random() < 0.5? 0: 1);
        if (value <= 1) {
            return value;
        }
    }
}

我们在这里所做的是生成额外的单个随机位,以将PRNG范围扩展到[0, 2)。然后,我们只需丢弃(1, 2)中的值,然后重试,直到我们的值达到[0, 1]

请注意,此方法平均调用Math.random() 4次。

或者,我们可以以1比特的精度将速度提高两倍:

var value = Math.random() * 2;

对于那些想要测试的人,这就是方法。只需假设Math.random()仅具有1位精度,并生成0或0.5。因此,在输出上,我们应该在值0、0.5和1之间具有均匀分布。

function random_inclusive_test (samples) {
    var hits = {};
    for (var i=0; i<samples; i++) {
        while (true) {
            var value =
                (Math.random() < 0.5? 0: 0.5) +
                (Math.random() < 0.5? 0: 1);
            if (value <= 1) {
                break;
            }
        }
        if (!hits[value]) {
            hits[value] = 1;
        }
        else {
            hits[value]++;
        }
    }
    console.log(hits);
}
random_inclusive_test(300000);

答案 8 :(得分:0)

我发现的解决方案是使用三角方程。

因为正弦波从Math.sin(0) = 0和Math.sin(90) = 1振荡。此过程一直重复到360度等于0。但是,360度仍然不是很精确,因此请使用{{1 }}。您只需要取绝对值即可消除负值。

所以

2 * Math.PI

答案 9 :(得分:0)

如果您确实需要生成 [0-1] 包含,可以通过指定精度来实现,例如生成具有包含限制的整数,然后再将其划分回来。
因为我们在计算机世界中,我建议使用 2 的幂的精度,这样最后的除法实际上不会触及数字。
除了 Number 本身是一个 64 位双精度数外,没有指定上限,这意味着 53 位有效数字。但是生成器本身是依赖于实现的,实际上它可能会也可能不会生成 0 到 1 之间的每个浮点数。

要实际查看生成的 0 和 1,通常需要低得多的精度,例如 20 位左右。这个限时(30 秒后停止)片段对我来说最多可以成功 25 位,有时甚至是 26 位。默认值是 23,这确实可以快速完成。

const precision=1<<parseInt(prompt("Bits (23)?") || "23");
console.log("hex:",precision.toString(16),"dec:",precision);
const limit=precision+1;
function next0to1() {
  return Math.floor(Math.random()*limit)/precision;
}

let start=Date.now();
let stop=start+30000;
let count=0;
while(Date.now()<stop){
  count++;
  if(next0to1()===0){
    console.log("0",count,Date.now()-start);
    break;
  }
}
while(Date.now()<stop){
  count++;
  if(next0to1()===1){
    console.log("1",count,Date.now()-start);
    break;
  }
}
console.log("attempts:",count);