2012-08-13 93 views
6

我是C#的新手。C#机会游戏

我所试图做的

我想在这里创造机会系统的游戏。

基本上,这是怎么回事:

我的问题:我如何做才能达到我所试图做的?

+0

听起来像你想生成一个随机数,并随机从列表,其中列表中的每个项目,被选中的加权机会选择一个项目也许是最简单的事情会是总结“机会”,并随机在0和sum(机会)之间,然后选择落在该数字上的项目。这是否正确? – 2012-08-13 23:33:53

+0

Random类有一个方法Next(int MaxValue)可能会对你有所帮助,对它进行一点搜索, – 2012-08-13 23:38:07

回答

5

您的示例代码有一个难题:您已经编写了150/208190/209。这是一个整数除法,并且两者的结果都是。你应该写下:150.0/208190.0/209来指示编译器将它们分为double的非整数。

编辑:
假设系统的RNG平整,而且你的表如下:

[item] [amount] 
0  3 000 000 
25  1 500 000 
50  2 000 000 
75  300 000 
100  10 000 
150  10 000 (no typo) 
    sum = 6820000 

那么你的随机数发生器可以看起来像:

int randomItemNumber = Random.Next(6820000); // 0..6819999 
if(randomItemNumber < 3000000) 
    Console.WriteLine("Aah, you've won the Item type #0\n"); 
else if(randomItemNumber < 3000000+1500000) 
    Console.WriteLine("Aah, you've won the Item type #1\n"); 
else if(randomItemNumber < 3000000+1500000+2000000) 
    Console.WriteLine("Aah, you've won the Item type #2\n"); 
else if(randomItemNumber < 3000000+1500000+2000000+300000) 
    Console.WriteLine("Aah, you've won the Item type #3\n"); 
else if(randomItemNumber < 3000000+1500000+2000000+300000+10000) 
    Console.WriteLine("Aah, you've won the Item type #4\n"); 
else if(randomItemNumber < 3000000+1500000+2000000+300000+10000+10000) 
    Console.WriteLine("Aah, you've won the Item type #5\n"); 
else 
    Console.WriteLine("Oops, somehow you won nothing, the code is broken!\n"); 

的想法是,你把所有一个接一个地在一条龙线上的项目,但是你把它们放在他们的小组中。所以,一开始有三百万第一种类型,然后是第二种类型的一千五百分之一,等等。该行共有6820000件物品。现在您随机选取一个从1到6820000(或从0到6819999)的数字,并将其用作LINE中元素的NUMBER。

由于这些项目与他们正确的统计分布一致,因此如果随机化1-6820000是FLAT,那么产生的“彩票”将具有与您想要的完全一样的分布。

剩下要解释的唯一技巧就是如何猜测挑选什么物品。这就是为什么我们将这些项目分组。 3000000项的第一部分是第一种类型,所以如果数量小于3000000,那么我们打到第一种类型。如果超过这个数字,但低于下一个1500000(低于4500000),那么第二个类型会被击中..等等。

+0

我已经这样做了,现在这个代码可以工作。谢谢。但是你认为这对我正在尝试做的事已经足够了吗?我对统计数据不太了解。 – Jack 2012-08-13 23:46:59

+0

你是一个很好的方式。我已经添加了一个很长的解释,说明'具有表格分布的生成器'是如何工作的。请重新阅读我的文章。 – quetzalcoatl 2012-08-13 23:54:48

+0

这似乎工作得很好。我已经尝试了一段时间。但是我想知道几件事情,如果问题太多,很抱歉。 ** 1 ** - 你的平均随机化意味着什么? ** 2 ** - 最终所有金额最终达到100%都无关紧要吗? ** 3 ** - 为什么我们不需要使用任何百分比机会?如果我们不这样做有什么关系? ** 4 ** - 无论如何我可以测试这个代码,如果我已经知道概率?例如,获得X的机会是80%,所以我将这段代码循环运行50次,然后查看它是否有效。例如,那么X应该在循环中显示80%左右,或者? – Jack 2012-08-14 11:48:51

0

我没有类似的东西在我的应用程序,将其转换成你的问题如下: 在伪代码:

  • 总结一下所有的值(以获得总)
  • 获取之间0随机值并且总和
  • 循环遍历所有项目,直到该项目总计所有值
  • 当到达随机数时,该项目是属于该值的项目。

类项目如下所示(去掉了一些不重要的线条和添加//

public class Items : List<Item> 
{ 
    public Items() 
    { 
     Add(new Item(0, 3000000)); 
     Add(new Item(25, 1500000)); 
     Add(new Item(50, 2000000)); 
     // etc 
    } 

    /// <summary> 
    /// Returns a random item based on value. 
    /// </summary> 
    /// <returns></returns> 
    public Item GetRandomItem() 
    { 
     var sum = this.Sum(item => item.Value); 
     var randomValue = new Random().Next(sum); 

     // Iterate through itemsuntil found. 
     var found = false; 
     var itemIndex = 0; 
     var visitedValue = 0; 
     while (!found) 
     { 
      var item = this[itemIndex]; 
      if ((visitedValue + item.Value) > randomValue) 
      { 
       found = true; 
      } 
      else 
      { 
       itemIndex++; 
       visitedValue += item.value;     
      } 
     } 

     return this[itemIndex];   
    } 

言论Item类无非是为名称和值的占位符多。

它看起来很长,但它有一些好处:

  • 当值的变化,总和自动进行计算。
  • 添加项目时,只需要更改一行。
1

正如其他人所说,你的代码有一个整数除法错误。

在任何情况下,您都需要查看:逆变换采样。

基本上,它允许你采取一个统一的随机数(大多数PRNG给你),并将其转换为任意分布的随机样本。为此,您需要使用目标分发的CDF。

参考&有用的网页:

[CiteHistory Record]

编辑: 我实际上是指分类分布,而不是多项分布。这两种分布通常会混淆(特别是在我的领域),但区别很重要。只有当多项分布参数化为n = 1时(即一次试验),这两种分布才是等价的。

0

一个除数必须是一个双因子,以防止零分。要计算你需要他们累积高达100%(或1)的概率:

//  Element  - Probability  - Cumulative Probability 
//  Item100  10000/6820000  0.001466275659824 
//  Item75  300000/6820000  0.0439882697947214 + 0.001466275659824 
//  Item50  2000000/6820000  0.2932551319648094 + 0.0454545454545454 
//  Item25  1500000/6820000  0.219941348973607 + 0.3387096774193548 
const double Item100 = 0.001466275659824; 
const double Item75 = 0.0454545454545454; 
const double Item50 = 0.3387096774193548; 
const double Item25 = 0.5586510263929618; 

int getRandomItem(Random rnd) 
{ 
    double value = rnd.NextDouble(); 
    if (value <= Item100) 
    { 
     // use one of both possible items (100 or 150) 
     int which = rnd.Next(0, 2); 
     return which == 0 ? 100 : 150; 
    } 
    else if (value <= Item75) 
     return 75; 
    else if (value <= Item50) 
     return 50; 
    else if (value <= Item25) 
     return 25; 
    else 
     return 0; 
} 

那你怎么使用它:

var rnd = new Random(); 
var items = new List<int>(); 
for (int i = 0; i < 100; i++) 
    items.Add(getRandomItem(rnd)); 
Console.Write(string.Join(Environment.NewLine, items)); 

请注意,我再利用随机实例。如果我在循环中创建它,“随机值将会是相同的,因为它会被同时播种。”

+0

我已经尝试过这样的事情,问题是25从来没有显示,而是50接管所有的时间。 – Jack 2012-08-14 11:52:05

+0

@Jack:我认为这种行为的原因是你总是在循环中使用一个新的随机实例。随机将播种当前时间。在一个循环中,它将始终是同一时间,因此,您将始终获得相同的“随机”值。这就是为什么我将随机实例作为参数传递给方法的原因。您应该在循环外部创建随机实例,并始终重复使用相同的实例。另一个选择是使随机成为该类中的成员变量。 – 2012-08-14 11:59:54

+0

@Jack:编辑我的答案以上面的演示,也改变了概率,因为它们需要累积。 – 2012-08-14 12:39:24

0

这样的事情应该会对你有所帮助。也许不是世界上最好的例子,但它应该足够了:

class Item 
{ 
    public string Name { get ; private set ; } 
    public int Amount { get ; private set ; } 

    public Item(string name , int amount) 
    { 
     if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("name") ; 
     if (amount < 0) throw new ArgumentException("amount") ; 

     this.Name = name ; 
     this.Amount = amount ; 

     return ; 
    } 
} 
static void Main(string[] args) 
{ 
    Random rng = new Random() ; 
    Item[] items = { new Item("item--0" , 3000000) , 
        new Item("item-25" , 1500000) , 
        new Item("item-50" , 2000000) , 
        new Item("item-75" , 300000) , 
        new Item("item-100" , 10000) , 
        new Item("item-150" , 10000) , 
        } ; 
    int total = items.Sum(x => x.Amount) ; 

    for (int i = 0 ; i < 100 ; ++i) 
    { 
     int r = rng.Next(0, total) ; // get a random integer x such that 0 <= x < total 
     int n = 0 ; 
     Item selected = null ; 
     int lo = 0 ; 
     int hi = 0 ; 
     for (int j = 0 ; j < items.Length ; ++j) 
     { 
      lo = n ; 
      hi = n + items[j].Amount ; 
      n = hi ; 

      if (r < n) 
      { 
       selected = items[j] ; 
       break ; 
      } 

     } 
     Console.WriteLine("iteration {0}. r is {1} <= {2} < {3}. Selected item is {4}" , 
      i , 
      lo , 
      r , 
      hi , 
      selected.Name 
      ) ; 


    } 

    return; 
}