2012-08-13 129 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; 
}