mae*_*ics 26 javascript c language-agnostic random
我们可以很容易地得到所需范围内的随机浮点数[X,Y)
(注意X是包含的,Y是独占的),下面列出了函数,因为Math.random()
(大多数伪随机数生成器,AFAIK)产生的数字在[0,1)
:
function randomInRange(min, max) {
return Math.random() * (max-min) + min;
}
// Notice that we can get "min" exactly but never "max".
Run Code Online (Sandbox Code Playgroud)
我们怎样才能得到一个包含两个边界的期望范围内的随机数,即[X,Y]
?
我想我们可以Math.random()
通过"滚动" IEE-754浮点双精度的位来"增加"我们的值(或等效),将最大可能值精确地设置为1.0,但这似乎很难实现,特别是语言不适合比特操纵.有没有更简单的方法?
(顺便说一句,为什么随机数生成器会产生数字[0,1)
而不是[0,1]
?)
[编辑]请注意,我没有必要这样做,我完全清楚这种区别是迂腐的.只是好奇并希望得到一些有趣的答案.如果这个问题不合适,请随意投票结束.
Ale*_*x L 13
我相信有更好的决定,但这个应该工作:)
function randomInRange(min, max) {
return Math.random() < 0.5 ? ((1-Math.random()) * (max-min) + min) : (Math.random() * (max-min) + min);
}
Run Code Online (Sandbox Code Playgroud)
首先,代码中存在问题:尝试randomInRange(0,5e-324)
或只需输入Math.random()*5e-324
浏览器的JavaScript控制台即可.
即使没有上溢/下溢/变形,也很难对浮点运算进行可靠的推理.经过一番挖掘,我可以找到一个反例:
>>> a=1.0
>>> b=2**-54
>>> rand=a-2*b
>>> a
1.0
>>> b
5.551115123125783e-17
>>> rand
0.9999999999999999
>>> (a-b)*rand+b
1.0
Run Code Online (Sandbox Code Playgroud)
更容易解释为什么在a = 2 53和b = 0.5 时发生这种情况:2 53 -1是下一个可表示的数字.默认的舍入模式("舍入到最接近的偶数")向上舍入2 53 -0.5(因为2 53是"偶数"[LSB = 0]而2 53 -1是"奇数"[LSB = 1]),所以你减去b
得到2 53,乘以得到2 53 -1,再加b
上得到2 53.
回答你的第二个问题:因为底层PRNG几乎总是在区间[0,2 n -1]中生成一个随机数,即它产生随机位.选择合适的n(浮点表示中的精度位)并除以2 n并获得可预测的分布非常容易.请注意,有一些数字[0,1)
,你永远不会使用这种方法生成((0,2-53)与IEEE双打的任何东西).
这也意味着你可以做a[Math.floor(Math.random()*a.length)]
而不用担心溢出(作业:在IEEE二进制浮点,证明b < 1
暗示a*b < a
为正整数a
).
另一个好处是,您可以将每个随机输出x视为表示区间[x,x + 2 -53)(不太好的是返回的平均值略小于0.5).如果你在[0,1]中返回,你是否以与其他所有相同的概率返回端点,或者它们是否只有一半的概率,因为它们只代表其他所有区间的一半?
为了回答在[0,1]中返回数字这个更简单的问题,下面的方法有效地生成一个整数[0,2 n ](通过在[0,2 n + 1 -1]中生成一个整数并将其丢弃,如果它太大了)除以2 n:
function randominclusive() {
// Generate a random "top bit". Is it set?
while (Math.random() >= 0.5) {
// Generate the rest of the random bits. Are they zero?
// If so, then we've generated 2^n, and dividing by 2^n gives us 1.
if (Math.random() == 0) { return 1.0; }
// If not, generate a new random number.
}
// If the top bits are not set, just divide by 2^n.
return Math.random();
}
Run Code Online (Sandbox Code Playgroud)
评论意味着基数2,但我认为这些假设是:
请注意,随机数总是成对生成:while
(a)中的随机数总是后跟一个中if
的一个或后一个中的一个(b).通过考虑返回0或0.5的PRNG来验证它是否合理是相当容易的:
a=0 b=0
:返回0a=0 b=0.5
:返回0.5a=0.5 b=0
:返回1a=0.5 b=0.5
:循环问题:
小智 5
我对这个问题的解决方案一直是使用以下代替你的上限.
Math.nextAfter(upperBound,upperBound+1)
Run Code Online (Sandbox Code Playgroud)
要么
upperBound + Double.MIN_VALUE
Run Code Online (Sandbox Code Playgroud)
所以你的代码看起来像这样:
double myRandomNum = Math.random() * Math.nextAfter(upperBound,upperBound+1) + lowerBound;
Run Code Online (Sandbox Code Playgroud)
要么
double myRandomNum = Math.random() * (upperBound + Double.MIN_VALUE) + lowerBound;
Run Code Online (Sandbox Code Playgroud)
这只是将您的上限增加最小的double(Double.MIN_VALUE
),以便在随机计算中包含您的上限.
这是一个很好的方法,因为它不会偏向任何一个数字的概率.
这不起作用的唯一情况是你的上限等于 Double.MAX_VALUE
考虑到 0 到 1 之间的值数量“非常多”,这真的很重要吗?实际达到 1的机会很小,因此它不太可能对您正在做的任何事情产生重大影响。
归档时间: |
|
查看次数: |
22219 次 |
最近记录: |