国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

解釋楊中科隨機(jī)數(shù)為什么會騙人?

2019-11-17 03:08:37
字體:
供稿:網(wǎng)友

解釋楊中科隨機(jī)數(shù)為什么會騙人?

當(dāng)你在Stack Overflow網(wǎng)站標(biāo)題中看到“隨機(jī)”這個詞你基本可以確定這是相同的基本問題無數(shù)的相似問題。本文帶你探討為什么隨機(jī)性會引起這么多問題并且如何解決它們。

Stack Overflow (or newsgroup, or mailing list etc) )網(wǎng)站的問題通常是這樣的:

我使用Random.Next生成隨機(jī)數(shù),但它一直給我相同的號碼。 它不停的運(yùn)行,但每次它會產(chǎn)生相同數(shù)量很多次。

這是由于這樣的代碼:

  // Bad code! Do not use!         for (int i = 0; i < 100; i++)         {                   Console.WriteLine(GenerateDigit());         }         ....         static int GenerateDigit()         {                   Random rng = new Random();                   // Assume there'd be more logic here really                   return rng.Next(10);         }

那么,這程序到底出了什么問題?

1.解讀

這種Random類不是真正的隨機(jī)數(shù)發(fā)生器,它是一個偽隨機(jī)數(shù)發(fā)生器。任何Random實(shí)例都有一定量的狀態(tài),而當(dāng)你調(diào)用Next( or NextDouble or NextBytes),它會使用該狀態(tài)來返回到似乎是隨機(jī)的數(shù)據(jù),相應(yīng)的改變它內(nèi)部狀態(tài)以便于在下一步調(diào)用時你將得到另一個偽隨機(jī)數(shù)。

所有的這一切都是確定的,如果你開始一個Random的實(shí)例以相同的初始狀態(tài)(可通過種子來提供),并使用相同的序列方法調(diào)用它,那你會得到相同的結(jié)果。

那么在我們的示例代碼中到底出了什么問題? 我們使用的一個新的Random實(shí)例也在循環(huán)迭代。隨機(jī)無參數(shù)的構(gòu)造函數(shù)取當(dāng)前日期和時間作為種子-在內(nèi)部定時器工作之前你通常可以執(zhí)行大量代碼,當(dāng)前的日期和時間就會發(fā)生變化。 因此,我們重復(fù)使用相同的種子就會重復(fù)得到相同的結(jié)果。

2.對此我們能做什么?

這個問題有很多的解決方案, 其中有些方法是比其他的更好。 讓我們先挑出其中一種方法,因?yàn)樗煌谄渌姆椒ā?/p>

3.使用加密的隨機(jī)數(shù)發(fā)生器

  .NET有一個RandomNumberGenerator類應(yīng)該是所有加密隨機(jī)數(shù)生成器派生而來的抽象類。 這個框架本身附帶了一個這樣的派生類: RNGCryptoServicePRovider 。 加密隨機(jī)數(shù)發(fā)生器的理念是,即使它可能仍然是一個偽隨機(jī)生成器,它還是很難做到不可預(yù)料。 內(nèi)置的實(shí)現(xiàn)需要多個熵源在你的電腦有效地呈現(xiàn)“噪音”,并難以預(yù)測。它可以使用這種噪音不僅僅是計(jì)算一個種子,也可以在生成下一個數(shù)字時讓你知道當(dāng)前的狀態(tài),這也許可能不足以預(yù)測下一個結(jié)果(或者那些已經(jīng)生成),這主要取決于具體的實(shí)施。Windows也可以利用專業(yè)硬件資源的隨機(jī)性(如一塊硬件觀察放射性同位素衰變),從而使得隨機(jī)數(shù)發(fā)生器更加安全。

  相比于這種隨機(jī),如果你看到(說)10個結(jié)果調(diào)用Random.Next(100)并投入大量計(jì)算資源任務(wù),你可能會制定出最初的種子并預(yù)知接下來的結(jié)果將是...很有可能也會知道之前的結(jié)果是什么。 如果這種隨機(jī)數(shù)應(yīng)用于證券或金融的目的,這會是災(zāi)難性的事態(tài)。 加密隨機(jī)數(shù)生成器通常比Random慢 ,但它在賦予數(shù)字難以預(yù)測和獨(dú)立方面做得更好。

  在很多情況下,隨機(jī)數(shù)生成器的性能不是一個問題-但有一個適當(dāng)?shù)腁PI就會出現(xiàn)問題。 隨機(jī)數(shù)字生成器設(shè)計(jì)基礎(chǔ)僅此是用來生成隨機(jī)字節(jié)。比較這種API的隨機(jī) ,它可以讓你請求一個隨機(jī)整數(shù),或隨機(jī)double,或一組隨機(jī)字節(jié)。我經(jīng)常發(fā)現(xiàn)我需要一個整數(shù)的范圍,得到可靠且一致地隨機(jī)字節(jié)數(shù)組是很重要的。這不是不可能,但至少你可能會想要一個適配器類在隨機(jī)數(shù)字生成器上。大多情況下,如果你能避免前面所述的陷阱,偽隨機(jī)性的Random是可以接受的。

  讓我們看看如何能做到這一點(diǎn)。

4.用一個復(fù)用的實(shí)例Random

對于“大量重復(fù)的數(shù)字”的修復(fù)程序的核心是重復(fù)使用同一個實(shí)例Random。 這聽起來很簡單...例如,我們可以改變我們這樣原始的代碼像這樣:

// Somewhat better code... Random rng = new Random();for (int i = 0; i < 100; i++) {     Console.WriteLine(GenerateDigit(rng)); } ...static int GenerateDigit(Random rng) {     // Assume there'd be more logic here really     return rng.Next(10); }

  現(xiàn)在,我們的循環(huán)會打印不同的數(shù)字......但我們還沒有完成。假如你在快速連續(xù)的時間內(nèi)調(diào)用此代碼會發(fā)生什么? 我們可能仍然需要創(chuàng)建的兩個Random實(shí)例使用相同的種子......雖然數(shù)字的每個字符串將包含不同的數(shù)字,我們可以很容易得到的數(shù)字相同的字符串的兩倍。

  有兩種方式可以避免這個問題。 一種方式是使用一個靜態(tài)字段保持的單個實(shí)例Random被每一個對象使用。另外,我們可以推高實(shí)例,當(dāng)然是最終達(dá)到計(jì)劃時,這永遠(yuǎn)只能實(shí)例化一個單一的元素隨機(jī)性 ,并將其傳遞到任意地方。這是一個不錯的主意(和它所表達(dá)的依賴性很好),但它不會完全的工作......至少,如果你的代碼使用多個線程它會引發(fā)問題。

5.線程安全

  Random不是線程安全的。這是一個真正的痛處,因?yàn)榭紤]到我們觀念上是想在任何程序中如何使用單個實(shí)例。 但事實(shí)是,如果你從多個線程使用相同實(shí)例,它很可能以全零內(nèi)部狀態(tài)結(jié)束,此時該實(shí)例變得無用。

  再次,在這里有兩種方法可以解決這個問題。其一是仍然使用一個實(shí)例, 而且使用的每個調(diào)用方必須記住他們所使用的隨機(jī)數(shù)生成器,同時獲得鎖。通過使用一個包裝器鎖定你就可以達(dá)到簡化的效果,但在一個高度多線程系統(tǒng)中你仍然有可能浪費(fèi)大量的時間等待加鎖。

  在這里我們將學(xué)會另一種方法 - 是讓每個線程有一個實(shí)例。 我們需要確保,當(dāng)我們創(chuàng)建實(shí)例時我們不要重復(fù)使用相同的種子(例如,所以我們不能只調(diào)用無參數(shù)的構(gòu)造函數(shù)),但除此之外它是相對簡單的。

6.一個安全驅(qū)動

  很幸運(yùn)的是,新ThreadLocal<T> .NET4類使得它很容易在每個線程需要單個實(shí)例中編寫提供者。 您只需給ThreadLocal<T>構(gòu)造一個委托調(diào)用來獲得初始值當(dāng)你不在的時候。 就我而言,我選擇使用一個單一的種子變量,初始化使用Environment.TickCount(就像參數(shù)的Random構(gòu)造函數(shù)),然后每遞增,我們需要一個新的隨機(jī)數(shù)生成器的時間-這是每一次的線程。

  整個類是靜態(tài)的,只有一種公開方法: 隨機(jī)獲得線程 。這是一個方法而不是一個屬性大多為方便起見:而不是讓其中需要隨機(jī)數(shù)的類依賴于Random本身,他們會依賴于Func<Random> 。 如果這類型僅設(shè)計(jì)在單個線程中運(yùn)行,它可以調(diào)用委托獲得的單個實(shí)例Random和重復(fù)使用; 假如它能夠從多個線程中每次使用調(diào)用委托它就需要一個隨機(jī)數(shù)發(fā)生器。 這將只會創(chuàng)造盡可能多的實(shí)例有線程,每個將使用不同的種子開始。 在依賴傳球的時候,我們就可以用一個方法轉(zhuǎn)換:

new TypeThatNeedsRandom(RandomProvider.GetThreadRandom) 下面的代碼:

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

  很簡單,不是嗎? 這是因?yàn)樗乃嘘P(guān)注的是提供正確的Random實(shí)例 。 它并不在乎你采用什么樣方法調(diào)用已經(jīng)獲取的實(shí)例。 代碼仍然可以濫用這個類,當(dāng)然,通過存放一個隨機(jī)引用并用多個線程重復(fù)使用它,但要做對的事還是很容易的。

7.界面設(shè)計(jì)問題

  一個問題仍然存在:這依舊不是很安全的。 正如我前面提到的,最常用的派生類是RNGCryptoServiceProvider,還有一個更安全隨機(jī)數(shù)字發(fā)生器的版本,然而這個API在一般情況下還是很難使用。

  假如框架驅(qū)動已經(jīng)從“我想以簡單的方法得到一個隨機(jī)值”的概念中分離概念的“隨機(jī)性源”,這確實(shí)是令人非常愉快的。 然后我們可以根據(jù)需要使用一個簡單的API來支持一個安全的或不安全的隨機(jī)源,很不幸的是,還沒有這樣的方法。也許在將來的迭代中......或者有個第三方會想出一個適配器來代替。(可惜這在我能力之上,很好地做好這件事情是相當(dāng)困難的。)你幾乎可以輕松成功地派生隨機(jī)和覆蓋示例及下個字節(jié) ......但目前還不清楚他們需要如何工作,甚至Sample可能會非常棘手。 也許下一次...

  這是一篇國外的文章,被我翻譯過來。原文地址:http://csharpindepth.com/Articles/Chapter12/Random.aspx

  接受批評指正,拒絕無腦噴糞。


上一篇:C#刪除圖片問題

下一篇:C# Sqlite事務(wù)

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 屏东县| 荆门市| 中方县| 张家港市| 大同市| 汾西县| 新民市| 绍兴县| 普洱| 岱山县| 揭西县| 聂拉木县| 景宁| 镇雄县| 开阳县| 班玛县| 大厂| 六安市| 碌曲县| 平武县| 洛宁县| 宁安市| 汉寿县| 盐城市| 大渡口区| 梨树县| 隆回县| 德江县| 江川县| 南投市| 宝丰县| 孟津县| 崇左市| 怀集县| 淅川县| 浠水县| 聂拉木县| 内丘县| 厦门市| 贵阳市| 荔波县|