当用户做某件事时,他们会得到2到100个单位。但是对于每1000个请求,我希望它加起来总共提供3500个单位。
下面是我为用户随机添加不同金额的代码:
if (Math.floor(Math.random() * 1000) + 1 === 900) {
//db call adding 100
}
else if (Math.floor(Math.random() * 100) + 1 === 90) {
//db call adding 40
}
else if (Math.floor(Math.random() * 30) + 1 === 20) {
//db call adding 10
}
else if (Math.floor(Math.random() * 5) + 1 === 4) {
//db call adding 5
}
else {
//db call adding 2
}
如果我的数学是正确的,这应该是平均每1000个呼叫大约4,332个单位。但很明显,它会变化,我不希望那样。我还希望它添加随机数量,因为在我的例子中添加的单位是任意的。
编辑:伙计们,Gildor是对的,我只是想要3500个单位,并在1000个请求内赠送它们。它甚至不完全需要总是达到3,500的最大值(我可以指定)。重要的是,我不会给用户太多的东西,同时创造机会让他们赢得更多的钱。这是我现在设置的,它工作得很好,并且通过一些调整会工作得更好:
调用外:
var remaining = 150;
var count = 0;
Inside of call:
count += 1;
if (count === 100) {
remaining = 150;
count = 0;
}
if (Math.floor(Math.random() * 30) + 1 === 20) {
var addAmount = Math.floor(Math.random() * 85) + 15;
if (addAmount <= remaining) {
remaining -= addAmount;
//db call adding addAmount + 2
}
else {
//db call adding 2
}
}
else if (Math.floor(Math.random() * 5) + 1 === 4) {
var addAmount1 = Math.floor(Math.random() * 10) + 1;
if (addAmount1 <= remaining) {
remaining -= addAmount1;
//db call adding addAmount1 + 2
}
else {
//db call adding 2
}
}
else {
//db call adding 2
}
我想我应该澄清一下,我想要一个很可能很小的"随机"数字。这是伎俩的一部分,你得到更大数额的概率很低。
正如我所评论的,1000个2到100之间的随机数加起来等于3500,平均值是3.5,这与2到100之间的随机选择不一致。你必须有几乎所有的2和3值才能达到这个目的,事实上,不能有超过两个大数字。一点都不随机。所以,为了使这个结果具有一定的随机性和可行性,你所选的总数必须远远大于3500。从2到100的1000个数字的随机总数更像是51000。
此外,您不能以真正随机的方式动态生成每个数字并保证特定的总数。保证结果的主要方法是预先分配随机数,这些随机数加起来等于已知实现该结果的总数,然后从预先分配的方案中随机选择每个数字,然后将其从未来选择的选择中删除。
你也可以试着保持一个运行总数,如果你偏离了你的总数,那么你的随机性就会发生偏差,但这样做的话,最后一组数字可能甚至不能接近随机,以便始终达到你的总数。
如果您重置总数以支持实际随机性(例如51,000),则可以使用的方案是预先分配一个由2到100之间的500个随机数组成的数组,然后添加另外500个作为这些随机数的补数的数字。这保证了51的平均值。然后,您可以从预分配的数组中随机选择每个数字,然后将其从数组中删除,这样它就不会再次被选中。我可以在一秒钟内添加代码来做到这一点。
function RandResults(low, high, qty) {
var results = new Array(qty);
var limit = qty/2;
var avg = (low + high) / 2;
for (var i = 0; i < limit; i++) {
results[i] = Math.floor((Math.random() * (high - low)) + low);
//
results[qty - i - 1] = (2 * avg) - results[i];
}
this.results = results;
}
RandResults.prototype.getRand = function() {
if (!this.results.length) {
throw new Error("getRand() called, but results are empty");
}
var randIndex = Math.floor(Math.random() * this.results.length);
var value = this.results[randIndex];
this.results.splice(randIndex, 1);
return value;
}
RandResults.prototype.getRemaining = function() {
return this.results.length;
}
var randObj = new RandResults(2, 100, 1000);
// get next single random value
if (randObj.getRemaining()) {
var randomValue = randObj.getRand();
}
一个真正随机选择的数字加起来等于51,000的工作演示(这是2到100之间的1,000个随机值的总和):http://jsfiddle.net/jfriend00/wga26n7p/
如果您想要的是以下内容:1,000个数字相加为3,500,并且从2到100(包括)之间选择,其中大多数数字将是2或3,但偶尔可能高达100,那么这是一个不同的问题。我不会用随机这个词来形容它,因为这是一个高度偏倚的选择。
这里有一个方法。它生成1000个2到100之间的随机数,并跟踪总数。然后,它通过随机选择的值来纠正随机数以达到正确的总数,并减少它们,直到总数减少到3500。您可以在这里看到它的工作原理:http://jsfiddle.net/jfriend00/m4ouonj4/
代码的主要部分如下:
function RandResults(low, high, qty, total) {
var results = new Array(qty);
var runningTotal = 0, correction, index, trial;
for (var i = 0; i < qty; i++) {
runningTotal += results[i] = Math.floor((Math.random() * (high - low)) + low);
}
// now, correct to hit the total
if (runningTotal > total) {
correction = -1;
} else if (runningTotal < total) {
correction = 1;
}
// loop until we've hit the total
// randomly select a value to apply the correction to
while (runningTotal !== total) {
index = Math.floor(Math.random() * qty);
trial = results[index] + correction;
if (trial >= low && trial <= high) {
results[index] = trial;
runningTotal += correction;
}
}
this.results = results;
}
这满足了偏置总数为3500且所有数字都在2到100之间的目标,尽管该方案中2
的概率非常高,而100
的概率几乎不存在。
这是一个加权随机生成器,它加起来是一个精确的总数。它使用立方加权方案来支持较小的数字(数字的概率随着数字的立方而下降),然后在生成随机数之后,校正算法对这些数字应用随机校正,以使总数完全符合指定。工作演示的代码在这里:http://jsfiddle.net/jfriend00/g6mds8rr/
function RandResults(low, high, numPicks, total) {
var avg = total / numPicks;
var i, j;
// calculate probabilities for each value
// by trial and error, we found that a cubic weighting
// gives an approximately correct sub-total that can then
// be corrected to the exact total
var numBuckets = high - low + 1;
var item;
var probabilities = [];
for (i = 0; i < numBuckets; i++) {
item = low + i;
probabilities[i] = avg / (item * item * item);
}
// now using those probabilities, create a steps array
var sum = 0;
var steps = probabilities.map(function(item) {
sum += item;
return sum;
});
// now generate a random number and find what
// index it belongs to in the steps array
// and use that as our pick
var runningTotal = 0, rand;
var picks = [], pick, stepsLen = steps.length;
for (i = 0; i < numPicks; i++) {
rand = Math.random() * sum;
for (j = 0; j < stepsLen; j++) {
if (steps[j] >= rand) {
pick = j + low;
picks.push(pick);
runningTotal += pick;
break;
}
}
}
var correction;
// now run our correction algorithm to hit the total exactly
if (runningTotal > total) {
correction = -1;
} else if (runningTotal < total) {
correction = 1;
}
// loop until we've hit the total
// randomly select a value to apply the correction to
while (runningTotal !== total) {
index = Math.floor(Math.random() * numPicks);
trial = picks[index] + correction;
if (trial >= low && trial <= high) {
picks[index] = trial;
runningTotal += correction;
}
}
this.results = picks;
}
RandResults.prototype.getRand = function() {
if (!this.results.length) {
throw new Error("getRand() called, but results are empty");
}
return this.results.pop();
}
RandResults.prototype.getAllRand = function() {
if (!this.results.length) {
throw new Error("getAllRand() called, but results are empty");
}
var r = this.results;
this.results = [];
return r;
}
RandResults.prototype.getRemaining = function() {
return this.results.length;
}
正如一些评论所指出的…问题中的数字不太有意义,但从概念上讲,有两种方法:及时动态计算或提前动态计算。
及时计算:
您可以维护一个remaining
变量,它跟踪3500个剩余的数量。每次当你随机给出一些单位时,从remaining
中减去数字,直到它变为0。
另外,为了确保每次至少给2个单位,可以从remaining = 1500
开始,每次给random + 2
单位。
为了防止在1000给出之后仍然有余额的情况,你可能需要添加一些逻辑,在最后几次给单位更多的侵略性。然而,它将导致不那么随机的结果。
提前计算:
在[2, 100]
中生成一个随机列表,包含1000个值,总和为3500。然后打乱列表。每次你想要给出一些单位时,选择数组中的下一个项目。在得到1000之后,以同样的方式生成另一个列表。这样你可以得到更好的随机结果。
请注意,这两种方法都需要某种共享状态,需要在多线程环境中小心处理。