设置等于加总数的随机变量C#

时间:2017-01-06 10:35:31

标签: c# algorithm math random

我正在制作一个游戏,其中有人打开胸部,胸部会给他们一个随机奖品。我可以给出的最大值是10,000个箱子中的85,000,000,平均值是8,500,但是我想要一些,所以一些箱子将低于这个值以上,并且能够设置最小损失2,500和最大赢取250,000但仍然得到总价值85,000,000。

我真的很难用我的C#知识为此想出一个算法。

5 个答案:

答案 0 :(得分:1)

伪代码算法:

  • 使用一系列箱子
    • 数组索引是胸号;数组长度是胸部数量
    • 数组中的值是指数中胸部的数量
    • 初始值是总金额除以箱数
  • 现在重复多次(比如说:胸部数量的10倍)
    • 得到两个随机的箱子
    • 计算出你可以从胸部1转移到胸部2的最大数量,这样1不会低于最低值,2不会超过最大值
    • 获得低于该最大值的随机值并将其传输

现在尝试在C#中实现它。

答案 1 :(得分:1)

这里有一些OOP。你有Player课程。其中存储了一些信息 - 他拥有的金额,待开的箱子以及他将要找到的箱子中的金币总量。

public class Player
{
    private int gold = 0;
    private int goldLeftInChests = 85000000;
    private int chestsToOpen = 10000;
    private Random random = new Random();

    public void OpenChest()
    {
        if (chestsToOpen == 0)
            return; // or whatever you want after 10000 chests.

        int goldInChest = CalculateGoldInNextChest();
        goldLeftInChests -= goldInChest;
        chestsToOpen--;
        gold += goldInChest;
    }

    private int CalculateGoldInNextChest()
    {
        if (chestsToOpen == 1)
            return goldLeftInChests;

        var average = goldLeftInChests / chestsToOpen;
        return random.Next(average);
    }
}

当下一个胸部被打开时,计算胸部的金币并调整玩家数据 - 我们向玩家添加一些金币并减少胸部的金币总量,然后将箱子打开。

计算胸部的黄金非常简单。我们得到平均金额并计算1到平均值之间的数字。第一次这个值总是低于8500.但是下一次平均值会稍微大一些。所以玩家将有机会找到超过8500.如果他再次不走运,平均值将会增长。或者如果palyer获得大量黄金,它将会减少。

更新:正如@Hans指出的那样,我没有计算箱子里金币的最小和最大限制。在@Hans解决方案中也存在一个问题 - 您应该在10000个箱子之间移动黄金很多时间以获得接近250000值的一些箱子。而且你必须填写并保留所有10000个值。我想到的下一个问题是.NET中的随机数字分布。值在我们使用的所有间隔上具有相等的概率。因此,如果我们从2500到250000产生价值,那么我们将获得8500(平均值)的价值的机会就像是12000(8500±6000)对比235500(250000-12000-2500)。这意味着从给定范围生成默认随机数将在开始时为您提供大量数字,然后您将保持在最低边界附近(2500)。所以你需要具有不同分布的随机数 - Gaussian variables。我们仍然希望8500金具有最高概率,而250000具有最低概率。这样的事情:

enter image description here

最后一部分 - 计算。我们只需要更新一种方法:)

private int CalculateGoldInNextChest()
{
    const int mean = 8500;
    var goldPerChestRange = new Range(2500, 250000);
    var averageRange = new Range(mean - 2500, mean + 2500);

    if (chestsToOpen == 1)
        return goldLeftInChests;

    do
    {
        int goldInChest = (int)random.NextGaussian(mu: mean, sigma: 50000);
        int averageLeft = (goldLeftInChests - goldInChest) / (chestsToOpen - 1);

        if (goldPerChestRange.Contains(goldInChest) && averageRange.Contains(averageLeft))
            return goldInChest;
    }
    while (true);
}

注意:我使用range来使代码更具可读性。多次运行测试会产生超过200000的最佳值。

答案 2 :(得分:0)

这应该是一个很好的起点。每个胸部随机填充限制,以确保剩余的胸部也可以获得有效值。

        Random rand = new Random();
        int[] chests = new int[numOffChests];
        int remaining = TotalValue;
        for(int i = 0; i < numOffChests; i++)
        {
            int minB = Math.Max(remaining / (numOffChests - i), maxChestValue);
            int maxB = Math.Min(remaining - (numOffChests - i * minChestValue), maxChestValue);
            int val = rand.Next(minB, maxB);
            remaining -= val;
            chests[i] = val;
        }

答案 3 :(得分:0)

分布必须严重偏离以获得具有该平均值的那个值范围。尝试使用指数公式X=exp(a*U+b)+c,其中U[0,1]上统一。那么条件是

 -2,500 = exp(b)+c
250,000 = exp(a+b)+c
  8,500 = integral(exp(a*u+b), u=0..1) 
        = exp(b)/a*(exp(a)-1)+c
        = 252,500/a+c

给出了两个方程式

250,000+2,500*exp(a) = c*(1-exp(a))
               8,500 = 252,500/a+c

一些图形和数字解决方案给出了#34;魔法&#34;数字

a =  22.954545, 
b = -10.515379, 
c = -2500.00002711621

现在根据该公式填充10,000个箱子,计算胸部价格的总和,并以任何你喜欢的模式分配,概率很小。

如果要更频繁地命中上限和下限,请在计算基础上增加界限,如果超出原始界限则剪切计算值。

答案 4 :(得分:0)

我假设概率函数给出了赢/输值V出现的机会。假设V的概率与(250000-V)** 2成正比,从而获得较高奖励的机会较少。

为了简化一些舍入问题,我们还假设输赢是100的倍数。然后您可以进行以下(未经测试的)计算:

int minwin = -2500 ;
int maxwin = 250000;
int chestcount = 10000;
int maxamount  = 85000;
// ----------- get probabilities for each win/lose amount to occur in all chests  ----------
long probtotal = 0 ;
List<long> prob = new List<long> ; 
for (long i=minwin;i<=maxwin;i++) if (i%100==0) 
{ long ii=(maxwin-i)*(maxwin-i) ; prob.Add((float)ii) ; probtotal+=ii ; }
for (int i=0;i<prob.Count;i++) prob[i]=prob[i]/(probtotal) ;
for (int i=0;i<prob.Count;i++) 
  Console.writeLine("Win/lose amount"+((i-minwin)*100).ToString()+" probability="+(proba[i]*100).ToString("0.000")) ;
// Transform "prob" so as to indicate the float count of chest corresponding to each win/lose amount
for (int i=0;i<prob.Count;i++) prob[i]=prob[i]*chestcount ;

// ---------- Set the 10000 chest values starting from the highest win -------------
int chestindex=0 ;
List<int> chestvalues = new List<int>();
float remainder = 0 ;
int totalwin=0 ;
for (int i=0;i<prob.Count;i++)
{
   int n = (int)(prob[i]+remainder)  ; // only the integer part of the float ;
   remainder = prob[i]+remainder-n ;    
   // set to n chests the win/lose amount
   int curwin=(i-minwin)*100 ;
   for (int j=0;j<n && chestvalues.count<chestcount;j++) chestvalues.Add(curwin) ; 
   totalwin+=curwin ;
}
// if stvalues.count lower than chestcount, create missing chestvalues  
for (int i=chestvalues.Count;i<chestcount;i++) chestvalues.Add(0) ;

// --------------- due to float computations, we perhaps want to make some adjustments --------------
// i.e. if totalwin>maxamount (not sure if it may happen), decrease some chestvalues
...
// --------------- We have now a list of 10000 chest values to be randomly sorted --------------
Random rnd = new Random();
SortedList<int,int> randomchestvalues = new SortedList<int,int>() ;
for (int i=0;i<chestcount;i++) randomchestvalues.Add(rnd.Next(0,99999999),chestvalues[i]) ;
// display the first chests amounts
for (int i=0;i<chestcount;i++) if (i<234) 
{ int chestamount = randomchestvalues.GetByIndex(i) ; Console.WriteLine(i.ToString()+":"+chestamount) ; }
}