Tại sao lại sử dụng System.Random của lớp C # thay vì System.Security.Cryptography.RandomNumberGenerator?


85

Tại sao mọi người lại sử dụng trình tạo số ngẫu nhiên "chuẩn" từ System.Random thay vì luôn sử dụng trình tạo số ngẫu nhiên an toàn bằng mật mã từ System.Security.Cryptography.RandomNumberGenerator (hoặc các lớp con của nó vì RandomNumberGenerator là trừu tượng)?

Nate Lawson nói với chúng tôi trong bài thuyết trình Google Tech Talk của anh ấy " Crypto Strikes Back " vào phút 13:11 không sử dụng các trình tạo số ngẫu nhiên "tiêu chuẩn" từ Python, Java và C # và thay vào đó sử dụng phiên bản bảo mật bằng mật mã.

Tôi biết sự khác biệt giữa hai phiên bản của trình tạo số ngẫu nhiên (xem câu hỏi 101337 ).

Nhưng cơ sở lý luận nào để không phải lúc nào cũng sử dụng trình tạo số ngẫu nhiên an toàn? Tại sao lại sử dụng System.Random? Hiệu suất có lẽ?


7
Bạn muốn nhập cái nào hơn?
Macha

13
Quá nhiều người nghiêm túc sử dụng điều đó như một lời biện minh cho những gì họ làm (thường không thành tiếng). Mã được đọc nhiều hơn được viết, ai quan tâm đến sự khác biệt nhỏ về độ dài?
Mark Sowul

3
Nhưng dù sao thì tại sao bạn nên sử dụng RNG mật mã nếu bạn không làm mật mã?
Mark Sowul

3
@Macha, đó là bí danh dành cho ->using R = System.Security.Cryptography.RandomNumberGenerator; R.Create();
cchamberlain

Câu trả lời:


144

Tốc độ và ý định. Nếu bạn đang tạo một số ngẫu nhiên và không cần bảo mật, tại sao lại sử dụng một hàm mật mã chậm? Bạn không cần bảo mật, vậy tại sao lại khiến người khác nghĩ rằng số đó có thể được sử dụng cho một thứ gì đó an toàn trong khi nó không có?


30
Tôi rất thích lập luận về ý định.
Lernkurve

12
Cần lưu ý rằng Random.GetNext còn lâu mới có khả năng "trải rộng" các số ngẫu nhiên trên phổ, đặc biệt là trong môi trường phân luồng. Tôi đã gặp phải vấn đề này khi viết một chương trình để kiểm tra các giải pháp khác nhau cho bài toán Rand7 từ Rand5. Trong một thử nghiệm phân luồng nhanh vừa rồi với 100000 số ngẫu nhiên từ 0 đến 10, 82470 số được tạo là 0. Tôi đã thấy sự khác biệt tương tự trong các thử nghiệm trước của mình. Mật mã ngẫu nhiên rất đồng đều trong sự phân bố số lượng của nó. Tôi đoán bài học là luôn kiểm tra dữ liệu ngẫu nhiên của bạn để xem nó "đủ ngẫu nhiên" cho nhu cầu của bạn.
Kristoffer L

35
@Kristoffer Tôi nghĩ bạn đã sử dụng sai Random. Hãy để tôi đoán: Bạn đã tạo một phiên bản mới của Randomlớp cho mỗi số, vì nó được tạo bởi một bộ đếm thời gian thô sẽ được tạo với cùng một giá trị trong khoảng thời gian khoảng 1-16ms.
CodesInChaos

15
@CodesInChaos: Bên cạnh đó, có một điều kiện chủng tộc Randomkhiến nó trả về tất cả các số 0 khi cùng một đối tượng được sử dụng từ nhiều luồng.
BlueRaja - Danny Pflughoeft

3
@KristofferL: Xem trên bình luận, cũng thấy câu trả lời này
BlueRaja - Danny Pflughoeft

65

Ngoài tốc độ và giao diện hữu ích hơn ( NextDouble()v.v.), cũng có thể tạo chuỗi ngẫu nhiên có thể lặp lại bằng cách sử dụng giá trị gốc cố định. Điều đó khá hữu ích, trong số những thứ khác trong quá trình Thử nghiệm.

Random gen1 = new Random();     // auto seeded by the clock
Random gen2 = new Random(0);    // Next(10) always yields 7,8,7,5,2,....

2
Và có BitConverter.ToInt32 (giá trị Byte [], int startIndex) có thể dễ hiểu hơn. ;)
sisve

7
Ian Bell và David Braben đã sử dụng một bộ tạo ngẫu nhiên trong trò chơi máy tính Elite để tạo ra một danh sách rộng lớn các hành tinh và các thuộc tính của chúng (kích thước, v.v.), với bộ nhớ rất hạn chế. Điều này cũng dựa vào việc trình tạo tạo ra một mẫu xác định (từ một hạt giống) - điều mà Crypto rõ ràng không cung cấp (theo thiết kế.) Có thêm một số thông tin về cách họ đã làm điều đó ở đây: wiki.alioth.net/index.php / Random_number_generator và cuốn sách "Vũ trụ trò chơi vô hạn: Kỹ thuật toán học" ISBN: 1584500581 có một cuộc thảo luận chung hơn về các kỹ thuật như vậy.
Daniel James Bryars


2
@phoog "Do đó, mã ứng dụng của bạn không nên giả định rằng cùng một hạt giống sẽ dẫn đến cùng một chuỗi giả ngẫu nhiên trong các phiên bản .NET Framework khác nhau." - Tôi không biết, có vẻ khá rõ ràng với tôi. Tuy nhiên, tôi sẽ không ngạc nhiên nếu họ không thể thay đổi nó trong thực tế mà không phá vỡ các chương trình hiện có, bất chấp cảnh báo này.
Roman Starkov

2
@phoog: Bạn đang nói một điều và sau đó hoàn toàn ngược lại. Bạn đang mâu thuẫn trực tiếp với chính mình.
Timwi

53

Trước hết, bản trình bày bạn đã liên kết chỉ nói về các số ngẫu nhiên cho mục đích bảo mật. Vì vậy, nó không phải Randomlà xấu cho các mục đích không phải bảo mật.

Nhưng tôi khẳng định là như vậy. Việc triển khai .net 4 Randomcó một số sai sót. Tôi khuyên bạn chỉ nên sử dụng nó nếu bạn không quan tâm đến chất lượng của các số ngẫu nhiên của mình. Tôi khuyên bạn nên sử dụng các triển khai của bên thứ ba tốt hơn.

Flaw 1: Gieo hạt

Hàm tạo mặc định bắt nguồn với thời gian hiện tại. Do đó, tất cả các trường hợp củaRandom được tạo bằng hàm tạo mặc định trong một khung thời gian ngắn (khoảng 10ms) đều trả về cùng một chuỗi. Đây là tài liệu và "theo thiết kế". Điều này đặc biệt khó chịu nếu bạn muốn đa luồng mã của mình, vì bạn không thể đơn giản tạo một phiên Randombản khi bắt đầu thực thi mỗi luồng.

Cách giải quyết là phải hết sức cẩn thận khi sử dụng hàm tạo mặc định và khởi tạo theo cách thủ công khi cần thiết.

Một vấn đề khác ở đây là không gian hạt giống khá nhỏ (31 bit). Vì vậy, nếu bạn tạo ra 50k trường hợp Randomvới các hạt giống hoàn toàn ngẫu nhiên, bạn có thể sẽ nhận được một dãy số ngẫu nhiên hai lần (do nghịch lý ngày sinh ). Vì vậy, gieo hạt thủ công cũng không dễ dàng thực hiện đúng.

Lỗi 2: Sự phân bố của các số ngẫu nhiên được trả về Next(int maxValue)là sai lệch

Có những thông số Next(int maxValue)rõ ràng không đồng nhất. Ví dụ, nếu bạn tính toán r.Next(1431655765) % 2bạn sẽ nhận được 0trong khoảng 2/3 số mẫu. (Mã mẫu ở cuối câu trả lời.)

Lỗi 3: NextBytes()Phương pháp không hiệu quả.

Chi phí cho mỗi byte NextBytes()lớn bằng chi phí để tạo một mẫu số nguyên đầy đủ vớiNext() . Từ điều này, tôi nghi ngờ rằng họ thực sự tạo ra một mẫu mỗi byte.

Việc triển khai tốt hơn bằng cách sử dụng 3 byte trong mỗi mẫu sẽ tăng tốc độ NextBytes() gần như hệ số 3.

Nhờ lỗ hổng Random.NextBytes()này chỉ nhanh hơn khoảng 25% so vớiSystem.Security.Cryptography.RNGCryptoServiceProvider.GetBytes máy của tôi (Win7, Core i3 2600MHz).

Tôi chắc rằng nếu ai đó kiểm tra mã nguồn / mã byte dịch ngược, họ sẽ tìm thấy nhiều sai sót hơn tôi đã tìm thấy với phân tích hộp đen của mình.


Mẫu mã

r.Next(0x55555555) % 2 rất thiên vị:

Random r = new Random();
const int mod = 2;
int[] hist = new int[mod];
for(int i = 0; i < 10000000; i++)
{
    int num = r.Next(0x55555555);
    int num2 = num % 2;
    hist[num2]++;
}
for(int i=0;i<mod;i++)
    Console.WriteLine(hist[i]);

Hiệu suất:

byte[] bytes=new byte[8*1024];
var cr=new System.Security.Cryptography.RNGCryptoServiceProvider();
Random r=new Random();

// Random.NextBytes
for(int i=0;i<100000;i++)
{
    r.NextBytes(bytes);
}

//One sample per byte
for(int i=0;i<100000;i++)
{   
    for(int j=0;j<bytes.Length;j++)
      bytes[j]=(byte)r.Next();
}

//One sample per 3 bytes
for(int i=0;i<100000;i++)
{
    for(int j=0;j+2<bytes.Length;j+=3)
    {
        int num=r.Next();
        bytes[j+2]=(byte)(num>>16);   
        bytes[j+1]=(byte)(num>>8);
        bytes[j]=(byte)num;
    }
    //Yes I know I'm not handling the last few bytes, but that won't have a noticeable impact on performance
}

//Crypto
for(int i=0;i<100000;i++)
{
    cr.GetBytes(bytes);
}

1
Thật thú vị, có thể xác nhận phát hiện của bạn: trên máy của tôi Next (1431655765) cũng cho 2/3 với bất kỳ gieo hạt nào. Điều kỳ diệu của 1431655765 là gì? Làm thế nào bạn đến con số này?
citykid

1
@citykid Xem số dưới dạng hex hoặc bit. Điều kỳ diệu nảy sinh từ cách đáng ngờ được Randomsử dụng để biến đổi một số nguyên 31 bit thành một số có giới hạn trên được chỉ định. Tôi quên chi tiết, nhưng nó giống như randomValue * max / 2^{31}.
CodesInChaos

1431655765_10 = 101010101010101010101010101010101_2
Tim S. Ngày

6
Hừm. Vậy bạn khuyên bạn nên sử dụng cách triển khai nào của Random cho C #?
Arsen Zahray

1
Chúa ơi, sự không đồng nhất của sự phân bố Next(), được bạn chứng minh ở đây, là một lỗi khá ngoạn mục - và vẫn còn tồn tại cho đến ngày nay, 6 năm sau khi bạn viết ra kết quả lần đầu tiên. (Tôi nói "lỗi" chứ không chỉ đơn thuần là "lỗ hổng", bởi vì tài liệu cho rằng "Số giả ngẫu nhiên được chọn với xác suất bằng nhau từ một tập hợp số hữu hạn" . Không phải vậy, và mã của bạn ở đây chứng minh điều đó.)
Mark Amery,

24

System.Random hoạt động hiệu quả hơn nhiều vì nó không tạo ra các số ngẫu nhiên an toàn bằng mật mã.

Một thử nghiệm đơn giản trên máy của tôi làm đầy bộ đệm 4 byte với dữ liệu ngẫu nhiên 1.000.000 lần mất 49 mili giây đối với Ngẫu nhiên, nhưng 2845 mili giây đối với RNGCryptoServiceProvider. Lưu ý rằng nếu bạn tăng kích thước của bộ đệm mà bạn đang lấp đầy, sự khác biệt sẽ thu hẹp vì chi phí cho RNGCryptoServiceProvider ít liên quan hơn.


2
Cảm ơn bạn đã chứng minh nó bằng một bài kiểm tra thực tế.
Lernkurve

3
Bạn có thể nghĩ rằng điều này là khắc nghiệt, nhưng -1 để đăng kết quả của điểm chuẩn hiệu suất mà không bao gồm mã của điểm chuẩn. Ngay cả khi các đặc điểm hiệu suất của RandomRNGCryptoServiceProviderkhông thay đổi trong 8 năm qua (điều mà tôi biết là có thể có), tôi đã thấy đủ các điểm chuẩn bị hỏng hoàn toàn được sử dụng trên Stack Overflow để không tin vào kết quả của một điểm chuẩn có mã không có sẵn công khai.
Mark Amery,

21

Các lý do rõ ràng nhất đã được đề cập đến, vì vậy đây là một lý do khó hiểu hơn: các PRNG mật mã thường cần được liên tục đặt lại bằng entropy "thực". Do đó, nếu bạn sử dụng CPRNG quá thường xuyên, bạn có thể làm cạn kiệt nhóm entropy của hệ thống, điều này (tùy thuộc vào việc triển khai CPRNG) sẽ làm suy yếu nó (do đó cho phép kẻ tấn công dự đoán nó) hoặc nó sẽ chặn trong khi cố gắng lấp đầy nhóm entropy của nó (do đó trở thành một vector tấn công cho một cuộc tấn công DoS).

Dù bằng cách nào, ứng dụng của bạn giờ đây đã trở thành một vectơ tấn công cho các ứng dụng khác, hoàn toàn không liên quan - không giống như ứng dụng của bạn - thực sự phụ thuộc vào các thuộc tính mật mã của CPRNG.

Đây là một vấn đề thực tế trong thế giới thực, BTW, đã được quan sát thấy trên các máy chủ không có đầu (vốn dĩ có vùng entropy khá nhỏ vì chúng thiếu các nguồn entropy như đầu vào chuột và bàn phím) chạy Linux, nơi các ứng dụng sử dụng sai /dev/randomhạt nhân CPRNG cho tất cả các loại của các số ngẫu nhiên, trong khi hành vi chính xác sẽ là đọc một giá trị hạt giống nhỏ từ đó /dev/urandomvà sử dụng giá trị đó để tạo ra PRNG của chính chúng .


Tôi đọc bài viết trên Wikipedia và một số nguồn Internet khác về entropy và sự suy giảm entropy, và tôi không hiểu lắm về nó. Làm cách nào tôi có thể làm cạn nhóm entropy khi trình tạo số ngẫu nhiên được cung cấp với thời gian hệ thống, số byte trống, v.v.? Làm thế nào những người khác có thể sử dụng nó như một vectơ tấn công để dự đoán các số ngẫu nhiên? Bạn có thể cung cấp một ví dụ dễ dàng? Có lẽ cuộc thảo luận này phải được thực hiện ngoại tuyến. en.wikipedia.org/wiki/Entropy_%28computing%29
Lernkurve

3
Thời gian hệ thống không phải là một nguồn entropy, vì nó có thể dự đoán được. Tôi không chắc về số byte miễn phí, nhưng tôi nghi ngờ đó cũng là một nguồn entropy chất lượng cao. Bằng cách gửi nhiều yêu cầu hơn đến máy chủ, kẻ tấn công có thể khiến số lượng byte miễn phí giảm xuống, khiến nó trở nên xác định một phần. Ứng dụng của bạn trở thành vectơ tấn công vì bằng cách cạn kiệt nhóm entropy, nó buộc ứng dụng khác, ứng dụng quan trọng về bảo mật phải sử dụng các số ngẫu nhiên ít hơn - hoặc đợi cho đến khi nguồn entropy được bổ sung.
quant_dev

Tôi hiểu rằng nếu một người có một trình tạo ngẫu nhiên giả được cung cấp ví dụ như một hạt giống 32 bit, thì một cuộc tấn công brute-force thường sẽ khá dễ dàng; thậm chí một hạt giống 64-bit có thể bị tấn công ngày sinh. Tuy nhiên, một khi hạt giống lớn hơn nhiều, tôi không hoàn toàn thấy rủi ro. Nếu một bộ tạo ngẫu nhiên cho mỗi byte đầu ra sẽ chuyển trạng thái 128 bit thông qua thuật toán mã hóa khối và sau đó xuất ra 8 bit dưới cùng, làm thế nào kẻ tấn công có thể ngay cả với hợp đồng biểu diễn byte đầu ra liên tiếp suy ra trạng thái, không có điểm yếu trong thuật toán mã hóa chính nó?
supercat

11

Nếu bạn đang lập trình một trò chơi bài trực tuyến hoặc trò chơi xổ số thì bạn sẽ muốn đảm bảo rằng chuỗi tiếp theo là không thể đoán được. Tuy nhiên, nếu bạn đang hiển thị cho người dùng, giả sử, một báo giá trong ngày về hiệu suất quan trọng hơn tính bảo mật.


9

Điều này đã được thảo luận ở một số thời điểm, nhưng cuối cùng, vấn đề hiệu suất là vấn đề cần cân nhắc thứ yếu khi lựa chọn RNG. Có rất nhiều RNG ngoài kia, và Lehmer LCG mà hầu hết các RNG hệ thống bao gồm không phải là tốt nhất hoặc thậm chí không nhất thiết phải là nhanh nhất. Trên các hệ thống cũ, chậm, đó là một sự thỏa hiệp tuyệt vời. Ngày nay, sự thỏa hiệp đó hiếm khi thực sự phù hợp. Điều này vẫn tồn tại trong các hệ thống ngày nay chủ yếu vì A) thứ đã được xây dựng và không có lý do thực sự nào để 'phát minh lại bánh xe' trong trường hợp này, và B) cho những gì mà phần lớn mọi người sẽ sử dụng nó, đó là 'đủ tốt'.

Cuối cùng, việc lựa chọn RNG phụ thuộc vào tỷ lệ Rủi ro / Phần thưởng. Trong một số ứng dụng, chẳng hạn như trò chơi điện tử, không có bất kỳ rủi ro nào. Lehmer RNG là quá đủ, và nhỏ gọn, ngắn gọn, nhanh chóng, dễ hiểu và 'trong hộp'.

Ví dụ: nếu ứng dụng là trò chơi poker trực tuyến hoặc xổ số, trong đó có các giải thưởng thực tế và tiền thật xuất hiện tại một thời điểm nào đó trong phương trình, thì Lehmer 'in the box' không còn đủ nữa. Trong phiên bản 32-bit, nó chỉ có 2 ^ 32 trạng thái hợp lệ có thể có trước khi bắt đầu quay vòng tốt nhất . Ngày nay, đó là một cánh cửa mở cho một cuộc tấn công vũ phu. Trong trường hợp như thế này, nhà phát triển sẽ muốn chuyển sang một thứ gì đó giống như RNG thời kỳ rất dài của một số loài và có thể gieo mầm nó từ một nhà cung cấp mạnh về mật mã. Điều này mang lại sự thỏa hiệp tốt giữa tốc độ và bảo mật. Trong trường hợp như vậy, người đó sẽ ra ngoài tìm kiếm thứ gì đó như Mersenne Twister , hoặc Multiple Recursive Generator .

Nếu ứng dụng giống như truyền thông số lượng lớn thông tin tài chính qua mạng, thì bây giờ sẽ có rủi ro rất lớn và nó vượt trội hơn nhiều so với bất kỳ phần thưởng nào có thể có. Vẫn có những chiếc xe bọc thép vì đôi khi những người đàn ông được trang bị vũ khí dày đặc là biện pháp an ninh duy nhất phù hợp, và tôi tin tưởng, nếu một lữ đoàn gồm những người đặc nhiệm với xe tăng, máy bay chiến đấu và trực thăng khả thi về mặt tài chính thì đó sẽ là phương pháp được lựa chọn. Trong trường hợp như thế này, sử dụng RNG mạnh về mặt mật mã là rất hợp lý, bởi vì bất kỳ mức độ bảo mật nào bạn có thể nhận được, nó đều không nhiều như bạn muốn. Vì vậy, bạn sẽ mất nhiều nhất có thể, và chi phí là một vấn đề rất xa xôi ở vị trí thứ hai, kể cả về thời gian hoặc tiền bạc. Và nếu điều đó có nghĩa là mỗi chuỗi ngẫu nhiên mất 3 giây để tạo trên một máy tính rất mạnh, bạn sẽ đợi 3 giây,


3
Tôi nghĩ rằng bạn đã sai về sự vĩ đại của bạn; gửi dữ liệu tài chính cần cực kỳ nhanh chóng; nếu thuật toán giao dịch của bạn có thể đạt được kết quả nhanh hơn 0,1ms so với đối thủ, thì bạn sẽ kết thúc tốt hơn trong hàng đợi lệnh mua / bán / cắt lỗ / báo giá. 3 giây là vĩnh cửu. Đây là lý do tại sao các nhà giao dịch đầu tư vào những máy tính cực kỳ tốt. Xem câu trả lời trước; Crypt.RNG chỉ mất 0,0028 ms cho mỗi số mới; 0,0000028 giây, vậy là bạn đã vượt xa 9 bậc về mức độ xử lý và tốc độ quan trọng như thế nào.
Henrik


4

Không phải ai cũng cần các số ngẫu nhiên an toàn bằng mật mã và họ có thể hưởng lợi nhiều hơn từ prng đơn giản nhanh hơn. Có lẽ quan trọng hơn là bạn có thể kiểm soát trình tự cho các số System.Random.

Trong một mô phỏng sử dụng các số ngẫu nhiên mà bạn có thể muốn tạo lại, bạn chạy lại mô phỏng với cùng một hạt giống. Nó có thể hữu ích cho việc theo dõi lỗi khi bạn cũng muốn tạo lại một tình huống bị lỗi nhất định - chạy chương trình của bạn với cùng một dãy số ngẫu nhiên chính xác đã làm hỏng chương trình.


2

Nếu tôi không cần bảo mật, tức là tôi chỉ muốn một giá trị tương đối không xác định chứ không phải một giá trị mạnh về mặt mật mã, Random có ​​giao diện dễ sử dụng hơn nhiều.


2

Các nhu cầu khác nhau yêu cầu các RNG khác nhau. Đối với tiền điện tử, bạn muốn các số ngẫu nhiên của mình càng ngẫu nhiên càng tốt. Đối với mô phỏng Monte Carlo, bạn muốn chúng lấp đầy không gian một cách đồng đều và có thể bắt đầu RNG từ một trạng thái đã biết.


1
Giá mà System.Random làm được thì .. ồ, tốt.
user2864740

2

Random không phải là một trình tạo số ngẫu nhiên, nó là một trình tạo chuỗi giả ngẫu nhiên xác định, lấy tên của nó vì lý do lịch sử.

Lý do để sử dụng System.Randomlà nếu bạn muốn các thuộc tính này, cụ thể là một chuỗi xác định, được đảm bảo tạo ra cùng một chuỗi kết quả khi được khởi tạo với cùng một hạt giống.

Nếu bạn muốn cải thiện "tính ngẫu nhiên" mà không phải hy sinh giao diện, bạn có thể kế thừa từ ghi System.Randomđè một số phương pháp.

Tại sao bạn muốn một chuỗi xác định

Một lý do để có một chuỗi xác định hơn là ngẫu nhiên thực sự là vì nó có thể lặp lại.

Ví dụ: nếu bạn đang chạy một mô phỏng số, bạn có thể khởi tạo chuỗi bằng một số ngẫu nhiên (đúng) và ghi lại số nào đã được sử dụng .

Sau đó, nếu bạn muốn lặp lại cùng một mô phỏng chính xác , ví dụ như cho mục đích gỡ lỗi, bạn có thể làm như vậy bằng cách khởi tạo chuỗi với giá trị được ghi lại .

Tại sao bạn muốn trình tự đặc biệt, không tốt lắm này

Lý do duy nhất tôi có thể nghĩ đến là khả năng tương thích ngược với mã hiện có sử dụng lớp này.

Tóm lại, nếu bạn muốn cải thiện trình tự mà không thay đổi phần còn lại của mã, hãy tiếp tục.


1

Tôi đã viết một trò chơi (Crystal Sliders trên iPhone: Đây ) sẽ đặt một loạt đá quý (hình ảnh) "ngẫu nhiên" trên bản đồ và bạn sẽ xoay bản đồ theo cách bạn muốn và chọn chúng và chúng biến mất. - Tương tự như Bejeweled. Tôi đang sử dụng Random (), và nó được tạo ra với số lượng 100 lần tích tắc kể từ khi điện thoại khởi động, một hạt giống khá ngẫu nhiên.

Tôi thấy thật ngạc nhiên khi nó sẽ tạo ra các trò chơi gần giống nhau - trong số 90 viên đá quý hoặc lâu hơn, có 2 màu, tôi sẽ nhận được hai màu CHÍNH XÁC giống nhau trừ 1 đến 3 viên đá quý! Nếu bạn lật 90 đồng xu và nhận được cùng một mẫu trừ 1-3 lần lật, điều đó RẤT khó xảy ra! Tôi có một số ảnh chụp màn hình cho thấy chúng giống nhau. Tôi đã bị sốc vì System.Random () tồi tệ như thế nào! Tôi đã giả định rằng tôi PHẢI viết một cái gì đó sai khủng khiếp trong mã của mình và đã sử dụng sai. Tuy nhiên, tôi đã sai, đó là máy phát điện.

Như một thử nghiệm - và là giải pháp cuối cùng, tôi quay trở lại trình tạo số ngẫu nhiên mà tôi đã sử dụng từ năm 1985 hoặc lâu hơn - công cụ này tốt hơn VASTLY. Nó nhanh hơn, có khoảng thời gian là 1,3 * 10 ^ 154 (2 ^ 521) trước khi nó lặp lại. Thuật toán ban đầu được tạo hạt với số 16 bit, nhưng tôi đã thay đổi số đó thành số 32 bit và cải thiện việc tạo hạt ban đầu.

Bản gốc ở đây:

ftp://ftp.grnet.gr/pub/lang/algorithm/c/jpl-c/random.c

Trong những năm qua, tôi đã ném mọi phép thử số ngẫu nhiên mà tôi có thể nghĩ ra về điều này và nó đã vượt qua tất cả chúng. Tôi không mong đợi rằng nó có bất kỳ giá trị nào dưới dạng mật mã, nhưng nó trả về một số nhanh như "return * p ++;" cho đến khi hết 521 bit, và sau đó nó chạy một quá trình nhanh chóng trên các bit để tạo ra các bit ngẫu nhiên mới.

Tôi đã tạo một trình bao bọc C # - được gọi là JPLRandom () đã triển khai giao diện giống như Random () và thay đổi tất cả những nơi tôi gọi nó trong mã.

Sự khác biệt hoàn toàn tốt hơn - Ôi trời, tôi rất ngạc nhiên - không có cách nào tôi có thể nhận ra nếu chỉ nhìn vào màn hình của 90 viên ngọc hoặc hơn trong một mẫu, nhưng tôi đã phát hành khẩn cấp trò chơi của mình sau đó.

Và tôi sẽ không bao giờ sử dụng System.Random () cho bất cứ thứ gì nữa. Tôi SỐC vì phiên bản của họ đã bị thổi bay bởi một thứ đã 30 năm tuổi!

-Traderhut trò chơi


3
Dự đoán đầu tiên của tôi là bạn đã tạo lại Randomquá thường xuyên. Nó chỉ nên được tạo một lần gọi Nexttrên trường hợp đó nhiều lần. Randomlà xấu, nhưng không phải xấu. Bạn có thể đăng một chương trình mẫu cùng với một cặp hạt giống thể hiện vấn đề này?
CodesInChaos

Mã này sẽ tạo ra một ngẫu nhiên () vào đầu mỗi cấp (nhưng đó là một vấn đề lớn với mức 1 hơn những người sau) Mã này đã xấp xỉ như sau:
Traderhut Games

Rnd = new Random ((uint) GameSeed); NextGameSeed = Rnd.Next (2000000000); Mỗi cấp độ sử dụng một Ngẫu nhiên mới được tạo ra với một hạt giống mới - Hạt giống đã được lưu cho mỗi cấp độ để tôi có thể tạo lại bản đồ và cũng xác nhận chuỗi hạt ngẫu nhiên đã khớp. Điều này cho phép tôi xác nhận rằng trò chơi là một loạt bản đồ hợp lệ đã được giải quyết và tạo lại trò chơi.
Traderhut Games

Và ban đầu, Random được tạo dựa trên System.DateTime.Now.Ticks (hoặc 0), sau đó GameSeed được chọn bằng cách sử dụng lệnh gọi tương tự như Rnd.Next () ở trên. Nếu tôi không thể làm điều này, thì có một vấn đề nghiêm trọng với việc tạo số ngẫu nhiên.
Traderhut Games

đây không phải là một câu trả lời cho câu hỏi ban đầu!
Mike Dinescu

-1

Vì System.Random bị đánh cắp ở đây vì "tính không chính xác" và thiên vị, tôi đã tự kiểm tra.

phân phối

Mã f # này chứng tỏ rằng nó hoạt động rất tốt - trên máy trung bình của tôi:

let r = System.Random()
Seq.init 1000000 (fun _ -> r.Next(0,10))
|> Seq.toList
|> Seq.groupBy id
|> Seq.map (fun (v,ls) -> v, ls |> Seq.length)
|> Seq.sortBy fst
|> Seq.iter (printfn "%A")

(0, 100208)
(1, 99744)
(2, 99929)
(3, 99827)
(4, 100273)
(5, 100280)
(6, 100041)
(7, 100001)
(8, 100175)
(9, 99522)    

Tất cả các phiên bản khung, máy, hệ điều hành đều có thể tạo ra sự khác biệt. Nhập mã trong F # tương tác trên máy của bạn và tự thử. Đối với Cyrptography tôi đã đọc

let arr = [| 0uy |]
let rr = System. Security.Cryptography.RandomNumberGenerator.Create()
Seq.init 1000000 (fun _ -> rr.GetBytes(arr); arr.[0])
|> Seq.toList
|> Seq.groupBy id
|> Seq.map (fun (v,ls) -> v, ls |> Seq.length)
|> Seq.sortBy fst
|> Seq.take 10 // show first 10 bytes
|> Seq.iter (printfn "%A")

// distribution of first 10 bytes
(0uy, 3862)
(1uy, 3888)
(2uy, 3921)
(3uy, 3926)
(4uy, 3948)
(5uy, 3889)
(6uy, 3922)
(7uy, 3797)
(8uy, 3861)
(9uy, 3874)

hiệu suất

#time

let arr = [| 0uy |]

let r = System.Random()
Seq.init 1000000 (fun _ -> r.NextBytes(arr); arr.[0] |> int64) |> Seq.sum

Real: 00:00:00.204, CPU: 00:00:00.203, GC gen0: 45, gen1: 1, gen2: 1
val it : int64 = 127503467L

let rr = System. Security.Cryptography.RandomNumberGenerator.Create()
Seq.init 1000000 (fun _ -> rr.GetBytes(arr); arr.[0] |> int64) |> Seq.sum

Real: 00:00:00.365, CPU: 00:00:00.359, GC gen0: 44, gen1: 0, gen2: 0
val it : int64 = 127460809L

điều này gợi ý mối quan hệ 1: 2 và hành vi bộ nhớ tốt hơn một chút của phiên bản tiền điện tử.

phần kết luận

Chủ yếu là vì API đẹp hơn nhiều của nó, phần nào là hiệu suất và khả năng phân phối khá tốt, System.Random được ưa thích hơn. System.Random cũng có thể giảm bớt sự phụ thuộc của thư viện và nếu một khung công tác được chuyển, System.Random có ​​thể sẽ có sẵn trước biến thể Crypto.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.