简体   繁体   English

在javascript中,如何在控制总金额的同时,向用户的余额添加随机数?

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

I'm trying to make it to where when a user does a certain thing, they get between 2 and 100 units. 我正在尝试使用户执行某项操作的位置达到2到100个单位。 But for every 1,000 requests I want it to add up to 3,500 units given collectively. 但是,对于每1000个请求,我希望它总共增加3,500个单位。

Here's the code I have for adding different amounts randomly to a user: 这是我用来向用户随机添加不同金额的代码:

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
}

If my math is correct, this should average around 4,332 units per 1,000 calls. 如果我的数学是正确的,那么这应该平均每1,000个调用大约4,332个单位。 But obviously it would vary and I don't want that. 但显然情况会有所不同,我不希望那样。 I'd also like it to add random amounts instead, as the units added in my example are arbitrary. 我还希望它添加随机数量,因为示例中添加的单位是任意的。

EDIT: Guys, Gildor is right that I simply want to have 3,500 units, and give them away within 1,000 requests. 编辑:伙计们,吉尔多是对的,我只想拥有3,500个单位,并在1,000个请求之内送出它们。 It isn't even entirely necessary that it always reaches that maximum of 3,500 either (I could have specified that). 甚至也不必完全达到最大值3500(我可能已经指定了)。 The important thing is that I'm not giving users too much, while creating a chance for them to win a bigger amount. 重要的是,我不会给用户太多的东西,而同时为他们创造机会赢得更大的收益。

Here's what I have set up now, and it's working well, and will work even better with some tweaking: 这是我现在设置的,它运行良好,并且通过一些调整会更好地工作:

Outside of call: 通话以外:

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
    } 

I guess I should have clarified, I want a "random" number with a high likelihood of being small. 我想我应该弄清楚,我想要一个很小的“随机”数。 That's kind of part of the gimmick, where you have low probability of getting a larger amount. 那是the头的一部分,在那儿您获得更大金额的可能性很小。

As I've commented, 1,000 random numbers between 2 and 100 that add up to 3,500 is an average number of 3.5 which is not consistent with random choices between 2 and 100. You'd have to have nearly all 2 and 3 values in order to achieve that and, in fact couldn't have more than a couple large numbers. 正如我评论过的,1,000个2到100之间的随机数加起来等于3500,这是平均数3.5,这与2到100之间的随机选择不一致。您必须将2和3的值几乎全部要实现这一目标,实际上最多只能有几个。 Nothing even close to random. 几乎没有随机的东西。 So, for this to even be remotely random and feasible, you'd have to pick a total much large than 3,500. 因此,要使它甚至是随机的和可行的,您必须选择比3500大得多的总数。 A random total of 1,000 numbers between 2 and 100 would be more like 51,000. 2至100之间的1000个随机总数将更像51,000。

Furthermore, you can't dynamically generate each number in a truly random fashion and guarantee a particular total. 此外,您不能以真正随机的方式动态生成每个数字并保证特定的总数。 The main way to guarantee that outcome is to pre-allocate random numbers that add up to the total that are known to achieve that and then random select each number from the pre-allocated scheme, then remove that from the choice for future selections. 保证结果的主要方法是预先分配随机数,这些总数加到已知的总数中,然后从预先分配的方案中随机选择每个数字,然后从选择中删除以供将来选择。

You could also try to keep a running total and bias your randomness if you get skewed away form your total, but doing it that way, the last set of numbers may have to be not even close to random in order to hit your total consistently. 如果您偏离总数,也可以尝试保持连续的总数并偏向随机性,但是这样做,最后一组数字可能甚至不必接近于随机数,才能始终如一地达到总数。

A scheme that could work if you reset the total to support what it should be for actual randomness (eg to 51,000) would be to preallocated an array of 500 random numbers between 2 and 100 and then add another 500 numbers that are the complements of those. 如果您重置总数以支持实际随机性(例如,达到51,000),那么可以使用的方案将是在2至100之间预先分配500个随机数的数组,然后再添加500个随机数的补集。 This guarantees the 51 avg number. 这保证了51平均数。 You can then select each number randomly from the pre-allocated array and then remove it form the array so it won't be selected again. 然后,您可以从预先分配的数组中随机选择每个数字,然后将其从数组中删除,这样就不会再次选择它。 I can add code to do this in a second. 我可以添加代码以在一秒钟内完成此操作。

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();
}

Working demo for a truly random selection of numbers that add up to 51,000 (which is what 1,000 random values between 2 and 100 should add up to): http://jsfiddle.net/jfriend00/wga26n7p/ 工作演示,用于真正随机选择的数字加起来为51,000(这是2到100之间的1,000个随机值应加起来的值): http : //jsfiddle.net/jfriend00/wga26n7p/


If what you want is the following: 1,000 numbers that add up to 3,500 and are selected from between the range 2 to 100 (inclusive) where most numbers will be 2 or 3, but occasionally something could be up to 100, then that's a different problem. 如果需要以下内容:1,000个数字加起来为3500,并从2到100(含)范围内选择,其中大多数数字为2或3,但有时最多为100,那么就不一样了问题。 I wouldn't really use the word random to describe it because it's a highly biased selection . 我不会真正使用随机这个词来形容它,因为它是一个高度偏向的选择

Here's a way to do that. 这是一种方法。 It generates 1,000 random numbers between 2 and 100, keeping track of the total. 它会生成2至100之间的1,000个随机数,并跟踪总数。 Then, afterwards it corrects the random numbers to hit the right total by randomly selected values and decrementing them until the total is down to 3,500. 然后,通过随机选择的值并将其递减,直到总数降低到3500,才校正随机数以达到正确的总数。 You can see it work here: http://jsfiddle.net/jfriend00/m4ouonj4/ 您可以在这里看到它的工作: http : //jsfiddle.net/jfriend00/m4ouonj4/

The main part of the code is this: 代码的主要部分是这样的:

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;
}

This meets an objective of a biased total of 3,500 and all numbers between 2 and 100, though the probability of a 2 in this scheme is very high and the probably of a 100 in this scheme is almost non-existent. 这符合一个偏共有3,500 2和100之间的所有数字的目标,虽然的概率2在这个方案是非常高,大概的100在这个方案几乎是不存在的。


And, here's a weighted random generator that adds up to a precise total. 而且,这是一个加权随机生成器,其总和为精确的总数。 This uses a cubic weighting scheme to favor the lower numbers (the probably of a number goes down with the cube of the number) and then after the random numbers are generated, a correction algorithm applies random corrections to the numbers to make the total come out exactly as specified. 这使用三次加权方案来支持较低的数字(一个数字的大概值随该数字的立方下降),然后在生成随机数之后,一种校正算法将随机校正应用于这些数字以使总数得出完全符合规定。 The code for a working demo is here: http://jsfiddle.net/jfriend00/g6mds8rr/ 一个有效的演示代码在这里: 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;
}

As some comments pointed out... the numbers in the question does not quite make sense, but conceptually there are two approaches: calculate dynamically just in time or ahead of time. 正如一些评论所指出的那样……问题中的数字不太有意义,但是从概念上讲,有两种方法:及时或提前进行动态计算。

To calculate just in time: 要及时计算:

You can maintain a remaining variable which tracks how many of 3500 left. 您可以维护一个remaining变量,该变量跟踪remaining的3500个变量。 Each time when you randomly give some units, subtract the number from remaining until it goes to 0. 每次当您随机分配一些单位时,请从remaining的数字中减去该数字,直到其变为0。

In addition, to make sure each time at least 2 units are given, you can start with remaining = 1500 and give random + 2 units each time. 另外,要确保每次至少给出2个单位,您可以从remaining = 1500开始,然后每次random + 2给出random + 2单位。

To prevent cases that after 1000 gives there are still balances left, you may need to add some logic to give units more aggressively towards the last few times. 为了防止在1000次赠予之后仍然剩余余额的情况,您可能需要添加一些逻辑以在最后几次更积极地分配单位。 However it will result in not-so-random results. 但是,它将导致不太随机的结果。

To calculate ahead of time: 要提前计算:

Generate a random list with 1000 values in [2, 100] and sums up to 3500. Then shuffle the list. [2, 100] 2,100 [2, 100]生成具有1000个值的随机列表,总和为3500。然后对列表进行混洗。 Each time you want to give some units, pick the next item in the array. 每次您要提供一些单位时,请选择数组中的下一项。 After 1000 gives, generate another list in the same way. 1000次捐赠后,以相同的方式生成另一个列表。 This way you get much better randomized results. 这样,您可以获得更好的随机结果。

Be aware that both approaches requires some kind of shared state that needs to be handled carefully in a multi-threaded environment. 请注意,这两种方法都需要某种共享状态,在多线程环境中需要谨慎处理。

Hope the ideas help. 希望这些想法有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM