在javascript中,我如何添加一个随机的金额到用户的余额,同时控制多少得到的总

In javascript, how do I add a random amount to a user's balance while controlling how much gets given total?

本文关键字:金额 余额 用户 多少 控制 javascript 何添加 一个 添加 随机      更新时间:2023-09-26

当用户做某件事时,他们会得到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之后,以同样的方式生成另一个列表。这样你可以得到更好的随机结果。

请注意,这两种方法都需要某种共享状态,需要在多线程环境中小心处理。