2011-08-31 99 views
5

我知道C#Random類不會使「真正的隨機」的數字,但我想出一個問題與此代碼:C#隨機數是不是「隨機」

public void autoAttack(enemy theEnemy) 
    { 
     //Gets the random number 
     float damage = randomNumber((int)(strength * 1.5), (int)(strength * 2.5)); 

     //Reduces the damage by the enemy's armor 
     damage *= (100/(100 + theEnemy.armor)); 

     //Tells the user how much damage they did 
     Console.WriteLine("You attack the enemy for {0} damage", (int)damage); 

     //Deals the actual damage 
     theEnemy.health -= (int)damage; 

     //Tells the user how much health the enemy has left 
     Console.WriteLine("The enemy has {0} health left", theEnemy.health); 
    } 

我然後調用函數在這裏(我把它叫做5次檢查的緣故,如果數字是隨機的):

 if (thePlayer.input == "fight") 
     { 
      Console.WriteLine("you want to fight"); 
      thePlayer.autoAttack(enemy1); 
      thePlayer.autoAttack(enemy1); 
      thePlayer.autoAttack(enemy1); 
     } 

然而,當我檢查輸出,我得到每3所函數調用的同一號碼。然而,每一個我運行程序時,我得到一個不同的號碼(重複3次)這樣的:

You attack the enemy for 30 damage. 
The enemy has 70 health left. 

You attack the enemy for 30 damage. 
The enemy has 40 health left. 

You attack the enemy for 30 damage. 
The enemy has 10 health left. 

我會再重建/調試/再次運行該程序,並獲得了不同數量,而不是30 ,但它會重複所有3次。

我的問題是:我怎麼能確保每個我調用這個函數的時間去不同的隨機數?我只是一遍又一遍地得到相同的「隨機」數字。

這裏是隨機級呼叫,我用:

private int randomNumber(int min, int max) 
    { 
     Random random = new Random(); 
     return random.Next(min, max); 
    } 
+4

你的'randomNumber'函數是什麼樣的? – Nija

+0

你可能想看看[這篇文章](http://www.codeducky.org/random-numbers-c-net-primer/),它討論了這個問題以及.NET Random類的其他陷阱 – ChaseMedallion

回答

26

我的猜測是,randomNumber創建的每次Random一個新的實例......這反過來又創造基於新的僞隨機數發生器目前的時間...不會像你想象的那麼頻繁地改變。

不要這樣做。重複使用Random的同一個實例...但是不需要通過創建靜態Random變量來「修復」它。這不會在長期內也無論是工作,因爲Random是不是線程安全的。它會在測試中看起來很好,然後你會神奇地得到所有的零後,你碰巧得到不幸的併發:(

幸運的是,使用線程本地機制來工作並不難,特別是如果你在.NET 4你最終的Random每個線程的新實例

我寫了一個article on this very topic這可能對你有用,包括下面的代碼:如果你改變new Random()通話

using System; 
using System.Threading; 

public static class RandomProvider 
{  
    private static int seed = Environment.TickCount; 

    private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random> 
     (() => new Random(Interlocked.Increment(ref seed))); 

    public static Random GetThreadRandom() 
    { 
     return randomWrapper.Value; 
    } 
} 

RandomProvider.GetThreadRandom()這可能會做一切你甲腎上腺素編輯(再次,假設.NET 4)。這並沒有解決可測試性,但一步一個腳印的時間...

+0

這怎麼可以修改爲使用問題中的最小值和最大值? – Julien

0

什麼是randomNumber

典型地,僞隨機數發生器被接種(與時間有關的事,或者一些隨機像兩個按鍵或網絡分組或東西之間的時間)。

您不會指出您使用的是什麼發生器,也不會說明它是如何接種的。

+0

我在示例中添加了我用於原始文章 – Mento

7

您沒有向我們顯示randomNumber的代碼。如果它看起來像

private int randomNumber(int m, int n) { 
    Random rg = new Random(); 
    int y = rg.Next(); 
    int z = // some calculations using m and n 
    return z; 
} 

那麼,那麼你的問題。如果您不斷創建Random的新實例,則有可能它們有時具有相同的種子(默認種子是精度有限的系統時鐘;足夠快地創建它們並獲得相同的種子),然後由此產生的序列發電機將永遠是一樣的。

爲了解決這個問題,你必須實例化Random實例一次:

private readonly Random rg = new Random(); 
private int randomNumber(int m, int n) { 
    int y = this.rg.Next(); 
    int z = // some calculations using m and n 
    return z; 
} 

,並清除了另一點,即使你做到這一點,從Random輸出仍然沒有「真」隨機的。這只是假冒。

+1

+1的隨機數類。但是,如果在非常短的時間內調用,它幾乎總是會產生相同的值,這更有可能...... ;-)「默認的種子值來源於系統時鐘並具有有限的分辨率。」 – 2011-08-31 01:42:58

+0

@pst,不,它不會產生同樣的價值。 'seed'(初始值)取決於時鐘,但是它的內部狀態在使用時會改變。 –

+0

@ J-16 SDiZ:我認爲pst的含義是,如果你有一個實例化Random類的新實例並返回一個隨機值並在循環中連續調用該方法的方法,你將看到相同的值重複了幾次,因爲種子沒有變化,然後隨着種子從時鐘變化變爲另一種值,重複了幾次,等等。 – jason

0

如果您在循環中生成隨機數,它可能不會是隨機的。因爲隨機數基本上是在當前系統時間內部創建的。 所以把這個代碼在循環:

Thread.Sleep(10); 

那麼系統將進入睡眠狀態,10分鐘秒。你會得到新的隨機數。 它是一個有保證的解決方案。但這也會影響系統的性能。

+0

在我困惑的時候,使用睡眠是我粗暴的解決方法,當我不斷得到2-3個重複的值並且感到困惑時。順便說一句,我仍然在(100)重複。在(500)處,它看起來是隨機的。但是,表現很糟糕。 @賈森上面的答案是一個很好的解決辦法。 – nanonerd

0

實例化方法外的隨機對象。 (Random random = new Random();應該寫在方法之前)

您也瞭解隨機不是really random也是至關重要的。