如何动态更改此随机数生成器的曲线? [英] How can I change the curve of this random number generator dynamically?
问题描述
此函数生成一个随机数,但其概率曲线偏向较低范围:
This function generates a random number but with a probability curve that favors the lower range:
function getRandomLowNumber(min=1,max=100,factor=1){
let num = getRandomDecimal(min,max);
let rollDiff = num - min;
let percent = (rollDiff) / (max - min);
percent = 1 - (1 - percent) / factor;
return Math.round(rollDiff * percent + min);
}
我希望它在决定曲线的地方起作用,因此1表示最小和最大范围内的所有数字均相等,但0.5表示下降,其中2的可能性是1的一半,而3的可能性是2的一半,依此类推。我在动态解决问题上遇到了很多麻烦。
I want it to work where the factor decides the curve, so a factor of 1 means all numbers along the min and max range are equally probable, but 0.5 means a drop-off where 2 is half as likely as 1 and 3 is half as likely as 2, and so on. I'm having a lot of trouble figuring it out dynamically though.
推荐答案
可以使用简单的算术函数来解决此问题。该函数将用于将具有特殊分布的均匀分布的随机数映射到所需范围。
It's possible to solve this with a simple arithmetical function. The function will be used to map an equally distributed random number to the desired range with the special distribution.
如果我们以您的示例为0.5,则每个后继对象应为一半很有可能,我们得到这样的事件集:
If we take your example with 0.5, which for each successor shall half it's probability, we get an event set like that:
#1 2 3 4 5 6 7
0 0 0 0 1 1 2
对于min = 0,max = 2和对于max = 3一样:
for min=0, max=2 and like that for max=3:
#1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
0 0 0 0 0 0 0 0 1 1 1 1 2 2 3
注意什么?在max = 2的情况下,该集合由7( 2 ^ 3-1
)个元素组成,在max = 3的情况下,它是15( 2 ^ 4-1
)。因此,对于任意的最大值,我们需要获取具有 2 ^(max + 1)-1
元素的事件集。
Notice something? In case of max=2 the set consists of 7 (2^3 - 1
) elements, and in case of max=3 it's 15 (2^4 - 1
). So for arbitrary max values we'd need to get event sets with 2^(max+1) - 1
elements.
所以我们现在要做的是
- 在范围
1 .. 2 ^(max + 1)-1
(等距) - 将此数字映射为相应的结果,例如在集合中显示
- create a random number in the range
1 .. 2^(max+1)-1
(equal distrib) - map this number to the corresponding result as shown in the set
第一个任务很简单,只需调用 getRandomNumber(1,2 ^(max + 1)-1)
。
第二个是通过计算以该随机数为底的对数并从差到最大值建立底数来完成的。
The first task is trivial, just call getRandomNumber(1,2^(max+1)-1)
.
The second one is done by computing the logarithm to base 2 of this random number and building the floor from the difference to max:
// for max==3 you get:
// rndnum : 1 2 3 4 5 6 7 8 9'0'1'2'3'4'5
// expflr : 0 1 1 2 2 2 2 3 3 3 3 3 3 3 3
// rndres : 3 2 2 1 1 1 1 0 0 0 0 0 0 0 0
int rndnum = getRandomNumber(1, Math.pow(2, max+1) - 1);
int expflr = Math.floor(log2(rndnum));
int rndres = max - expflr;
如果 min!= 0
怎么办?< br>
这很容易处理:我们只需从max中减去它,然后将其添加到最终结果中即可。
What if min != 0
?
This is easy to handle: We just subtract it from max and add it to the final result.
除0.5以外的分布是什么?
我们在公式中看到的臭名昭著的2就是 1 / 0.5
。对于任意值,我们将2替换为 1 / factor
(主题中的因子,范围从0到1)。
事件数集合中的值由(1 / factor)^ 0 +(1 / factor)^ 1 +(1 / factor)^ 2 + ...
给出(((1 /因子)^(max + 1)-1)/(1/1 /因子-1)
。
What with distributions other than 0.5?
The notorious 2 that we saw in the formula is nothing but 1/0.5
. For an arbitrary value we replace the 2 with 1/factor
(factor from your topic, ranges from 0 exclusive to 1).
The number of events in the set is given by (1/factor)^0 + (1/factor)^1 + (1/factor)^2 + ...
which is equal to ((1/factor)^(max+1) - 1) / (1/factor - 1)
.
适用于最小,最大和系数的最终版本:
Final version that works with arbitrary min, max and factor:
double base = 1.0 / factor;
int evtcnt = Math.floor(Math.pow(base, max-min+1) - 1) / (base-1));
int rndnum = getRandomNumber(1, evtcnt);
int expflr = Math.floor(logb((rndnum-1) * (base-1) + 1, base));
int rndres = max - expflr;
请注意, log2
和 logb
在Java中不存在,但是您可以使用 log10(val)/ log10(2.0)
resp log10(val)/ log10(base)
。
Note that log2
and logb
do not exist in Java, but you can define them with log10(val) / log10(2.0)
resp log10(val) / log10(base)
.
function logb(val, base) {
return Math.log10(val) / Math.log10(base);
}
function getRandomNumber(min,max){
return Math.floor(Math.random()*(max-min+1)+min);
}
function getRandomLowNumber(min, max, factor) {
var base = 1.0 / factor;
var evtcnt = Math.floor(Math.pow(base, max-min+1) - 1) / (base-1);
var rndnum = getRandomNumber(1, evtcnt);
var expflr = Math.floor(logb((rndnum-1) * (base-1) + 1, base));
var rndres = max - expflr;
return rndres;
}
function runit() {
var min = document.getElementById('input-min').value;
var max = document.getElementById('input-max').value;
var factor = document.getElementById('input-factor').value;
var times = document.getElementById('input-times').value;
var list = {};
for (let i = 0; i < times; i++) {
var number = getRandomLowNumber(min, max, factor);
if (typeof list[number] == 'number') {
list[number]++;
} else {
list[number] = 1;
}
}
console.log('Min: ', min);
console.log('Max: ', max);
console.log('Factor: ', factor);
console.log('Iterations: ', times);
console.log('List: ', list);
}
function runClippy() {
var name = 'Clippy';
if (clippy.load._data[name]) {
return;
}
clippy.load(name, function(agent) {
var animations = agent.animations();
$('.js-states').text(animations.join(' '));
agent.show();
agent.moveTo(400, 30);
agent.speak("Hello, I see you're trying to run this sample. My name is " + name + " and yacc sent me here to help.");
agent.moveTo(200, 100);
agent.speak("There are four input fields to put parameters.");
agent.moveTo(300, 50);
agent.gestureAt(-100,50);
agent.speak("The first two specify the minimum and maximum random value.");
agent.moveTo(90,50);
agent.gestureAt(0,-50);
agent.speak("I'll put starting values for you here.");
agent._addToQueue(function(complete) {
$('.input-min').val("1");
$('.input-max').val("100");
complete();
});
agent.gestureAt(-100,50);
agent.speak("The next field specifies the factor that will decrease the probability for each successor. It should range between 0 (exclusively) and 1. Let's try a value here.");
agent._addToQueue(function(complete) {
$('.input-factor').val("0.5");
complete();
});
agent.moveTo(550, 70);
agent.gestureAt(-100,50);
agent.speak("The final input field is used to specify the amount of random numbers to generate. I'll fill it in for you.");
agent._addToQueue(function(complete) {
$('.input-times').val("100");
complete();
});
agent.speak("Now, did you notice the big button at the bottom of the form? You can push it to start the calculation.");
agent.moveTo(50, 120);
agent.gestureAt(-100,50);
agent.moveTo(90,50);
agent.gestureAt(0,-50);
agent.speak("Be careful with the amount of calculations. If the task takes too long, it might be aborted.");
agent.moveTo(630, 200);
agent.speak("So, now you can start on your own calculation of randoms. Be sure to fill in the fields properly, so that min <= max, or 0 < factor <= 1. Our lab is so busy at the moment that we spared a few safety belts.");
agent._addToQueue(function(complete) {
$('.wmd-input').val("# What are you trying to achieve?");
complete();
});
agent.moveTo(400, 30);
agent.gestureAt(-100, 50);
agent.speak("Please describe in short what you are trying to achieve");
agent._addToQueue(function(complete) {
$('.wmd-input').val("# What are you trying to achieve?\n\n# What is the problem you're facing?");
complete();
});
agent.moveTo(400, 70);
agent.gestureAt(-100, 50);
agent.speak("Please describe the error you're getting, and/or post the error message you're getting");
agent._addToQueue(function(complete) {
$('.wmd-input').val("# What are you trying to achieve?\n\n# What is the problem you're facing?\n\n#Show the code causing the problem");
complete();
});
agent.moveTo(400, 90);
agent.gestureAt(-100, 50);
agent.speak("Please post the code that causes your problem. Try to post it without clutter or unrelated code.");
agent.speak("People who answer should be able to use your code to reproduce the error. Please lookup MVCE in the stack overflow help .");
agent.moveTo(630, 200);
});
}
$(document).ready(function(){
// $('.wmd-input').one('focus', function() {runClippy();});
$('.input-min').one('focus', runClippy);
$('.input-max').one('focus', runClippy);
$('.input-factor').one('focus', runClippy);
$('.input-times').one('focus', runClippy);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel='stylesheet prefetch' href='https://cdn.rawgit.com/smore-inc/clippy.js/master/build/clippy.css'>
<!--link rel="stylesheet prefetch" href="https://cdn.sstatic.net/Sites/stackoverflow/all.css?v=b0fb54f66683"-->
<script src='https://cdn.rawgit.com/smore-inc/clippy.js/master/build/clippy.min.js'></script>
<div>
Min: <input value="0" id="input-min" class="input-min processed">
Max: <input value="100" id="input-max" class="input-max processed"><br>
Factor: <input value="0.5" id="input-factor" class="input-factor processed"><br>
#-Runs: <input value="1000000" id="input-times" class="input-times processed"><br>
<button style="float: center;" onclick="runit()">--- Run that algorithm ---</button>
</div>
这篇关于如何动态更改此随机数生成器的曲线?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!