我想知道是否有算法生成随机数,这些随机数很可能在从min到max的范围内很低。例如,如果您生成1到100之间的随机数,如果您使用f(min: 1, max: 100, avg: 30)
调用该函数,则大部分时间应该低于30,但如果您使用f(min: 1, max: 200, avg: 10)
调用该函数,则平均值应该最高10.许多游戏都是这样做的,但我找不到用公式做到这一点的方法。我见过的大多数例子都使用了“drop table”或类似的东西。
我已经提出了一种相当简单的方法来衡量一个滚动的结果,但它效率不高而且你没有很多控制它
var pseudoRand = function(min, max, n) {
if (n > 0) {
return pseudoRand(min, Math.random() * (max - min) + min, n - 1)
}
return max;
}
rands = []
for (var i = 0; i < 20000; i++) {
rands.push(pseudoRand(0, 100, 1))
}
avg = rands.reduce(function(x, y) { return x + y } ) / rands.length
console.log(avg); // ~50
该函数只需在最小和最大N次之间选取一个随机数,其中每次迭代都会在最后一次滚动时更新最大值。因此,如果您使用N = 2, and max = 100
调用它,那么它必须连续两次滚动100才能返回100
我已经在维基百科上看了一些发行版,但我不太了解它们,知道如何控制最小和最大输出等。
非常欢迎任何帮助
答案 0 :(得分:8)
生成具有给定分布的随机数的简单方法是从列表中选择一个随机数,其中应更频繁出现的数字根据所需的分布重复。
例如,如果您创建列表[1,1,1,2,2,2,3,3,3,4]
并从0
到9
选择一个随机索引,从该列表中选择一个元素,您将得到一个<4
个数字概率为90%。
或者,使用上面示例中的分布,生成数组[2,5,8,9]
并从0
到9
选择一个随机整数,如果它是≤2
(这将以30%的概率发生)然后返回1,如果它>2
和≤5
(这也会以30%的概率发生)返回2
等等。 / p>
答案 1 :(得分:3)
概率分布函数只是一个函数,当你输入一个值X时,它将返回获得该值X的概率。累积分布函数是得到一个小于或等于X的数字的概率。 CDF是PDF的组成部分。 CDF几乎总是一对一的功能,因此它几乎总是具有反转。
要生成PDF,请绘制x轴上的值和y轴上的概率。所有概率的和(离散)或积分(连续)应加起来1.找到一些能正确模拟该方程的函数。为此,您可能需要查找一些PDF。
基本算法
https://en.wikipedia.org/wiki/Inverse_transform_sampling
此算法基于逆变换采样。 ITS背后的想法是你在CDF的y轴上随机选取一个值并找到它对应的x值。这是有道理的,因为随机选择一个值的可能性越大,空间越多&#34;它将占据CDF的y轴。
步骤1到3是你必须硬编码到游戏中的东西。围绕它的任何PDF的唯一方法是求解与其平均值相对应的形状参数,并保持对形状参数所需的约束。如果要使用Kumaraswamy分布,则将其设置为使形状参数a和b始终大于1。
我建议使用Kumaraswamy发行版,因为它是有界的,它有一个非常好的封闭形式和封闭形式反转。它只有两个参数a和b,它非常灵活,因为它可以模拟许多不同的场景,包括多项式行为,钟形曲线行为,以及在两个边都有峰值的盆状行为。此外,使用此功能进行建模并不困难。形状参数b越高,向左倾斜的越多,形状参数a越高,向右倾斜的越多。如果a和b都小于1,则分布看起来像一个槽或盆。如果a或b等于1,则分布将是不会将凹度从0改变为1的多项式。如果a和b都等于1,则分布是直线。如果a和b大于1,则函数看起来像钟形曲线。您可以做的最好的事情是实际绘制这些函数的图形,或者只运行逆变换采样算法。
https://en.wikipedia.org/wiki/Kumaraswamy_distribution
例如,如果我想要一个形状如此的概率分布,其中a = 2且b = 5从0到100:
https://www.wolframalpha.com/input/?i=2*5*x%5E(2-1)*(1-x%5E2)%5E(5-1)+from+x%3D0+to+x%3D1
其CDF将是:
CDF(x)= 1-(1-X ^ 2)^ 5
它的反转是:
CDF ^ -1(X)=(1-(1-X)^(1/5))^(1/2)
Kumaraswamy分布的一般逆向是: CDF ^ -1(X)=(1-(1-X)^(1 / B))^(1 / A)
然后我会生成一个从0到1的数字,将它放入CDF ^ -1(x),并将结果乘以100.
<强>赞成强>
<强>缺点强>
答案 2 :(得分:1)
您可以组合2个随机过程。例如:
第一个兰特R1 = f(最小值:1,最大值:20,平均值:10); 第二个兰特R2 = f(最小值:1,最大值:10,平均值:1);
然后乘以R1 * R2得到[1-200]和平均值大约10之间的结果(平均值会稍微偏移)
另一种选择是找到要使用的随机函数的反函数。此程序必须在程序启动时初始化,但不需要重新计算。这里使用的数学可以在很多数学库中找到。我将逐一解释一个未知随机函数的例子,其中只知道四个点:
希望最后一部分是众所周知的,你应该能够找到一个可以进行这种计算的库(参见wiki)。如果积分函数的逆矩阵没有闭合形式解(如任何五度或更高的一般多项式),则可以使用根Newton's Method等根寻找方法。
这种计算可用于创建任何类型的随机分布。
编辑:
你可以在wikipedia中找到上面描述的逆变换采样,我发现了implementation(我还没有尝试过。)
答案 3 :(得分:1)
我会使用一个简单的数学函数。根据您的描述,您需要一个指数级数,如y = x ^ 2。平均值(平均值是x = 0.5,因为兰特得到的数字从0到1)你会得到0.25。如果你想要一个较低的平均数,你可以使用更高的指数,如y = x ^ 3,在x = 0.5时会导致y = 0.125 例: http://www.meta-calculator.com/online/?panel-102-graph&data-bounds-xMin=-2&data-bounds-xMax=2&data-bounds-yMin=-2&data-bounds-yMax=2&data-equations-0=%22y%3Dx%5E2%22&data-rand=undefined&data-hideGrid=false
PS:我调整了函数来计算所需的指数以得到平均结果。 代码示例:
function expRand (min, max, exponent) {
return Math.round( Math.pow( Math.random(), exponent) * (max - min) + min);
}
function averageRand (min, max, average) {
var exponent = Math.log(((average - min) / (max - min))) / Math.log(0.5);
return expRand(min, max, exponent);
}
alert(averageRand(1, 100, 10));
答案 4 :(得分:0)
您可以保持到目前为止从函数返回的运行平均值,并基于while循环中的运行平均值获得满足平均值的下一个随机数,调整运行平均值并返回数字
答案 5 :(得分:0)
使用丢弃表允许非常快速的滚动,这在实时游戏中很重要。实际上,它只是一个范围内的一个数字的随机生成,然后根据概率表(该范围的高斯分布)和多个选择的if语句。这样的事情:
num = random.randint(1,100)
if num<10 :
case 1
if num<20 and num>10 :
case 2
...
它不是很干净但是当你的选择数量有限时,它可以非常快。
答案 6 :(得分:0)
有很多方法可以做到这一点,所有这些方法基本上都归结为从右边生成 - skewed(a.k.a. positive-skewed)分布。您没有明确表示是否需要整数或浮点结果,但是存在适合该法案的离散和连续分布。
最简单的选择之一是离散的或连续的右边 - triangular distribution,但是虽然这会让你逐渐减少对更大值的渴望,但它不会让你独立控制平均值。 / p>
另一种选择是截断指数(对于连续)或几何(对于离散)分布。你需要截断,因为原始指数或几何分布的范围从零到无穷大,所以你必须砍掉上尾。这反过来要求你做一些微积分来找到一个速率λ,它在截断后产生所需的平均值。
第三种选择是使用分布的混合,例如,在较低范围内以一定概率p均匀地选择数字,并且在概率较高的范围内选择一个数字(1-p)。然后,总平均值是较低范围的平均值的p倍乘以上限范围的平均值的+(1-p)倍,并且您可以通过调整范围和p的值来拨入所需的总体均值。如果对子范围使用非均匀分布选择,则此方法也适用。这一切都归结为你愿意为推导出适当的参数选择投入多少工作。
答案 7 :(得分:0)
一种方法不是最精确的方法,但根据您的需要可以被视为“足够好”。
算法将是在最小和最大滑动之间选择一个数字。保证最大g_max
和潜在最大p_max
。您的真实最大值将根据另一个随机调用的结果滑动。这将为您提供您正在寻找的偏差分布。以下是Python中的解决方案。
import random
def get_roll(min, g_max, p_max)
max = g_max + (random.random() * (p_max - g_max))
return random.randint(min, int(max))
get_roll(1, 10, 20)
以下是使用(1,10,20)运行100,000次的函数的直方图。
答案 8 :(得分:0)
private int roll(int minRoll, int avgRoll, int maxRoll) {
// Generating random number #1
int firstRoll = ThreadLocalRandom.current().nextInt(minRoll, maxRoll + 1);
// Iterating 3 times will result in the roll being relatively close to
// the average roll.
if (firstRoll > avgRoll) {
// If the first roll is higher than the (set) average roll:
for (int i = 0; i < 3; i++) {
int verificationRoll = ThreadLocalRandom.current().nextInt(minRoll, maxRoll + 1);
if (firstRoll > verificationRoll && verificationRoll >= avgRoll) {
// If the following condition is met:
// The iteration-roll is closer to 30 than the first roll
firstRoll = verificationRoll;
}
}
} else if (firstRoll < avgRoll) {
// If the first roll is lower than the (set) average roll:
for (int i = 0; i < 3; i++) {
int verificationRoll = ThreadLocalRandom.current().nextInt(minRoll, maxRoll + 1);
if (firstRoll < verificationRoll && verificationRoll <= avgRoll) {
// If the following condition is met:
// The iteration-roll is closer to 30 than the first roll
firstRoll = verificationRoll;
}
}
}
return firstRoll;
}
<强>解释强>
<强>优点:强>
<强>缺点:强>
<强>测试强>
您可以将以下方法与roll()
- 方法结合使用来对此进行测试。数据保存在散列图中(将数字映射到出现次数)。
public void rollTheD100() {
int maxNr = 100;
int minNr = 1;
int avgNr = 30;
Map<Integer, Integer> numberOccurenceMap = new HashMap<>();
// "Initialization" of the map (please don't hit me for calling it initialization)
for (int i = 1; i <= 100; i++) {
numberOccurenceMap.put(i, 0);
}
// Rolling (100k times)
for (int i = 0; i < 100000; i++) {
int dummy = roll(minNr, avgNr, maxNr);
numberOccurenceMap.put(dummy, numberOccurenceMap.get(dummy) + 1);
}
int numberPack = 0;
for (int i = 1; i <= 100; i++) {
numberPack = numberPack + numberOccurenceMap.get(i);
if (i % 10 == 0) {
System.out.println("<" + i + ": " + numberPack);
numberPack = 0;
}
}
}
结果(100.000卷):
这些都是预期的。请注意,您可以随时微调结果,只需修改roll()
- 方法中的迭代计数(平均值应该越接近30,应该包含的迭代次数越多(请注意,这可能会损害表现到一定程度))。另请注意,到目前为止,30(是预期的)具有最高出现次数的数字。
答案 9 :(得分:0)
仿真软件(如SLX)用于创建伪数据的算法就是所谓的线性同余生成器。 Wikipedialink here。
那里的等式以及explenation非常好。您必须将此等式写入方法并设置相应的返回值。
答案 10 :(得分:0)
试试这个, 为低于平均值的数字范围生成一个随机数,并为高于平均值的数字范围生成第二个随机数。
然后随机选择其中一个,每个范围将在50%的时间内被选中。
Bundle extras = new Bundle();
final ArrayList<CustomObject> objects = new ArrayList<CustomObject>();
objects.add(new CustomObject("Squat", "65%", "6", "150", false));
extras.putSerializable("w29w1", objects);
Intent intent = new Intent(getApplicationContext(), WorkoutDaysActivity.class);
intent.putExtra("args", extras);
startActivity(intent);
答案 11 :(得分:0)
看到很多好的解释和一些好的想法,我仍然认为这可以帮助你:
您可以在0
附近使用任何分发函数 f ,并将您感兴趣的时间间隔替换为所需的时间间隔[1,100]
: f - &gt ;的 F'强>
然后使用 f'的结果提供C++
discrete_distribution。
我在下面有一个正态分布的示例,但我无法将结果输入此函数:-S
#include <iostream>
#include <random>
#include <chrono>
#include <cmath>
using namespace std;
double p1(double x, double mean, double sigma); // p(x|x_avg,sigma)
double p2(int x, int x_min, int x_max, double x_avg, double z_min, double z_max); // transform ("stretch") it to the interval
int plot_ps(int x_avg, int x_min, int x_max, double sigma);
int main()
{
int x_min = 1;
int x_max = 20;
int x_avg = 6;
double sigma = 5;
/*
int p[]={2,1,3,1,2,5,1,1,1,1};
default_random_engine generator (chrono::system_clock::now().time_since_epoch().count());
discrete_distribution<int> distribution {p*};
for (int i=0; i< 10; i++)
cout << i << "\t" << distribution(generator) << endl;
*/
plot_ps(x_avg, x_min, x_max, sigma);
return 0; //*/
}
// Normal distribution function
double p1(double x, double mean, double sigma)
{
return 1/(sigma*sqrt(2*M_PI))
* exp(-(x-mean)*(x-mean) / (2*sigma*sigma));
}
// Transforms intervals to your wishes ;)
// z_min and z_max are the desired values f'(x_min) and f'(x_max)
double p2(int x, int x_min, int x_max, double x_avg, double z_min, double z_max)
{
double y;
double sigma = 1.0;
double y_min = -sigma*sqrt(-2*log(z_min));
double y_max = sigma*sqrt(-2*log(z_max));
if(x < x_avg)
y = -(x-x_avg)/(x_avg-x_min)*y_min;
else
y = -(x-x_avg)/(x_avg-x_max)*y_max;
return p1(y, 0.0, sigma);
}
//plots both distribution functions
int plot_ps(int x_avg, int x_min, int x_max, double sigma)
{
double z = (1.0+x_max-x_min);
// plot p1
for (int i=1; i<=20; i++)
{
cout << i << "\t" <<
string(int(p1(i, x_avg, sigma)*(sigma*sqrt(2*M_PI)*20.0)+0.5), '*')
<< endl;
}
cout << endl;
// plot p2
for (int i=1; i<=20; i++)
{
cout << i << "\t" <<
string(int(p2(i, x_min, x_max, x_avg, 1.0/z, 1.0/z)*(20.0*sqrt(2*M_PI))+0.5), '*')
<< endl;
}
}
如果我让他们绘制以下结果:
1 ************
2 ***************
3 *****************
4 ******************
5 ********************
6 ********************
7 ********************
8 ******************
9 *****************
10 ***************
11 ************
12 **********
13 ********
14 ******
15 ****
16 ***
17 **
18 *
19 *
20
1 *
2 ***
3 *******
4 ************
5 ******************
6 ********************
7 ********************
8 *******************
9 *****************
10 ****************
11 **************
12 ************
13 *********
14 ********
15 ******
16 ****
17 ***
18 **
19 **
20 *
所以 - 如果你能把这个结果提供给discrete_distribution<int> distribution {}
,你得到了你想要的一切......
答案 12 :(得分:0)
好吧,从我能看到你的问题,我希望解决方案符合这些标准:
a)属于单个发行版:如果我们需要在每个函数调用中多次“滚动”(调用math.Random)然后聚合或丢弃某些结果,它将根据给定的函数停止真正分发。 / p> b)不是计算密集型的:有些解决方案使用积分(Gamma分布,高斯分布),而且计算量很大。在您的描述中,您提到您希望能够“使用公式计算”,这符合此描述(基本上,您需要O(1)函数)。
c)相对“分布均匀”,例如没有峰值和谷值,而是大多数结果聚集在均值周围,并且向下朝向末端具有良好的可预测斜率,但是具有最小和最大值的概率不为零。
d)不要求在内存中存储大型数组,如drop table。
我认为这个功能符合要求:
var pseudoRand = function(min, max, avg )
{
var randomFraction = Math.random();
var head = (avg - min);
var tail = (max - avg);
var skewdness = tail / (head + tail);
if (randomFraction < skewdness)
return min + (randomFraction / skewdness) * head;
else
return avg + (1 - randomFraction) / (1 - skewdness) * tail;
}
这将返回浮点数,但您可以通过调用
轻松将它们转换为整数 (int) Math.round(pseudoRand(...))
它在我的所有测试中返回了正确的平均值,并且它也很好地分发到最终。希望这可以帮助。祝你好运。