Có thể chấp nhận dựa vào ints ngẫu nhiên là duy nhất?


42

Tôi đã thực hiện một giao thức mạng và tôi yêu cầu các gói phải có các định danh duy nhất. Cho đến nay, tôi chỉ đang tạo các số nguyên 32 bit ngẫu nhiên và cho rằng về mặt thiên văn không có khả năng sẽ xảy ra xung đột trong suốt tuổi thọ của chương trình / kết nối. Đây thường được coi là một thông lệ có thể chấp nhận được trong mã sản xuất, hay người ta nên nghĩ ra một hệ thống phức tạp hơn để ngăn ngừa va chạm?


47
Tại sao sử dụng một số nguyên tuần tự sẽ không cắt nó?
whatsisname

20
Tại sao bạn không sử dụng int tăng dần? GUID , được thiết kế để có các thuộc tính duy nhất mà bạn mô tả, có kích thước 128 bit, không phải 32.
Robert Harvey

21
Hoặc, chỉ định số kênh cho mỗi máy tính được kết nối và sử dụng id chuỗi tăng dần. Hai số kết hợp (với số kênh chiếm các bit thứ tự cao) trở thành id duy nhất mới của bạn.
Robert Harvey

27
Nếu "trình tạo số ngẫu nhiên" của bạn đảm bảo rằng một số cụ thể sẽ không được lặp lại cho đến khi mọi số khác được tạo, thì đó là trình tạo số ngẫu nhiên rất kém! Theo cùng một logic, chuỗi tung đồng xu "ngẫu nhiên" duy nhất có thể là HTHTHTHTHT ....
alephzero

17
"Tôi yêu cầu các gói phải có số nhận dạng duy nhất" Hậu quả của yêu cầu này bị vi phạm là gì? Nếu bạn yêu cầu số nhận dạng duy nhất, trong khi đọc từ nghiêm ngặt nhất, bạn phải có một hệ thống định danh loại bỏ hệ thống tập trung (như cách MAC được gán cho các công ty thẻ mạng riêng lẻ). Nhiều khả năng bạn có một định nghĩa nhẹ nhàng hơn về "yêu cầu". Hiểu rằng mức độ mềm mại sẽ thay đổi đáng kể các câu trả lời bạn nhận được.
Cort Ammon

Câu trả lời:


142

Coi chừng nghịch lý sinh nhật .

Giả sử bạn đang tạo một chuỗi các giá trị ngẫu nhiên (thống nhất, độc lập) từ một tập hợp kích thước N (N = 2 ^ 32 trong trường hợp của bạn).

Sau đó, quy tắc ngón tay cái cho nghịch lý sinh nhật nói rằng một khi bạn đã tạo về các giá trị sqrt (N), có ít nhất 50% khả năng xảy ra va chạm, nghĩa là có ít nhất hai giá trị giống nhau trong trình tự tạo.

Với N = 2 ^ 32, sqrt (N) = 2 ^ 16 = 65536. Vì vậy, sau khi bạn đã tạo khoảng 65k số nhận dạng, nhiều khả năng hai trong số chúng có va chạm với nhau không! Nếu bạn tạo một định danh mỗi giây, điều này sẽ xảy ra trong vòng chưa đầy một ngày; Không cần phải nói, nhiều giao thức mạng hoạt động nhanh hơn thế.


11
+1. Trong công việc cuối cùng của tôi, một trong những đối tác của chúng tôi thực sự đã sử dụng phương pháp này để tạo các số nhận dạng ngẫu nhiên (không phải cho các gói mạng, mà cho một đối tượng kinh doanh chia sẻ cuối cùng được tạo bởi khách hàng cuối). Khi tôi truy vấn dữ liệu bằng con mắt về điều này, tôi thấy rằng trung bình, có hai đến ba cặp trùng lặp mỗi ngày. (May mắn thay, điều này điều duy nhất phá sản nếu các bản sao được tạo ra trong vòng bốn giờ của nhau, mà đã xảy ra một chút ít thường xuyên hơn Nhưng vẫn còn..)
ruakh

6
(bấm vào đây để kết xuất toán học) Đối với giá trị của nó, phép tính gần đúng $ \ sqrt {N} $ là chính xác đến một yếu tố không đổi; với $ N = 2 ^ {32} $, ngưỡng thực tế là 77164, vì đây là giá trị nhỏ nhất của $ n $ sao cho $ \ prod_ {k = 1} ^ {n-1} (1 - k / N) <1 / 2. $
wchargein

4
@wchargein: Thực sự không có gì kỳ diệu về xác suất đạt 0,5; Điều đáng chú ý là xác suất tăng tương đối nhanh khi tăng N. Nếu số nhận dạng 32 bit có khả năng xảy ra va chạm ngẫu nhiên nhưng không tầm thường, thì số nhận dạng 40 bit sẽ gần như không có.
supercat

3
@supercat: Đó là tất cả sự thật. Tôi chỉ hình dung rằng nếu một người cung cấp một hằng số như vậy, thì người ta cũng có thể đưa ra một giá trị chính xác :-)
wchargein

2
@wchargein: Tôi thích suy nghĩ về việc người ta cần bắt đầu lo lắng về các bản sao. Nếu một người đi xuống dưới sqrt (N) nhiều thì xác suất va chạm giảm xuống nhanh chóng, đến mức người ta có thể nói rằng chúng sẽ không xảy ra một cách an toàn trừ khi có lỗi nghiêm trọng trong trình tạo ngẫu nhiên.
supercat

12

Nó được coi là chấp nhận được khi dựa vào các số ngẫu nhiên là duy nhất nếu những số đó có đủ bit. Có các giao thức mật mã trong đó việc lặp lại một số ngẫu nhiên sẽ phá vỡ toàn bộ bảo mật. Và miễn là không có lỗ hổng nghiêm trọng nào trong trình tạo số ngẫu nhiên đang được sử dụng, đó không phải là vấn đề.

Một trong những thuật toán để tạo UUID sẽ tạo ra một ID hiệu quả bao gồm 122 bit ngẫu nhiên và cho rằng nó sẽ là duy nhất. Và hai trong số các thuật toán khác dựa trên giá trị băm được cắt ngắn thành 122 bit là duy nhất, có nguy cơ va chạm gần như nhau.

Vì vậy, có các tiêu chuẩn dựa trên 122 bit là đủ để tạo một ID ngẫu nhiên duy nhất, nhưng 32 bit chắc chắn là không đủ. Với ID 32 bit, chỉ mất khoảng 2¹⁶ ID trước khi nguy cơ va chạm lên tới 50% vì với 2¹⁶ ID sẽ có gần 2³¹ cặp mỗi cặp có thể xảy ra va chạm.

Thậm chí 122 bit là ít hơn tôi muốn giới thiệu trong bất kỳ thiết kế mới. Nếu tuân theo một số tiêu chuẩn hóa là quan trọng đối với bạn, thì hãy sử dụng UUID. Nếu không, sử dụng một cái gì đó lớn hơn 122 bit.

Hàm băm SHA1 với đầu ra 160 bit không còn được coi là an toàn, một phần vì 160 bit không đủ để đảm bảo tính duy nhất của đầu ra. Các hàm băm hiện đại có đầu ra từ 224 đến 512 bit. ID được tạo ngẫu nhiên phải nhắm đến các kích thước giống nhau để đảm bảo tính duy nhất với mức an toàn tốt.


12
SHA-1 được coi là không an toàn vì có các cuộc tấn công cụ thể (tức là không ngẫu nhiên) đối với chính thuật toán có thể tìm thấy va chạm nhanh hơn lực lượng vũ phu, không phải vì có khả năng xảy ra va chạm ngẫu nhiên cao. Một ước tính sơ bộ nói rằng với 122 bit và tốc độ tạo 1 tỷ (10 ^ 9) ID mỗi giây, sẽ mất hơn 73 năm trước khi đạt được 50% khả năng xảy ra va chạm.
8bittree

sqrt(2^122)= 2,3 triệu triệu triệu UUID
noɥʇʎԀʎzɐɹƆ

2
@ 8bittree Mạng bitcoin tính toán 2⁷⁰ SHA2 băm sau mỗi 10 phút. Nếu đó là băm SHA1, nó sẽ chỉ mất một tuần để tạo ra một vụ va chạm. Nếu các UUID được tạo ra với cùng tốc độ mà bitcoin tính toán băm thì sẽ mất ít hơn 2 giây để tạo ra xung đột.
kasperd

Bitcoin là tất cả về việc cố gắng tìm kiếm sự va chạm, và vô cùng phổ biến và đã có phần cứng chuyên dụng được thiết kế dành riêng cho việc tìm kiếm băm. Bây giờ, chắc chắn, nếu OP đang có kế hoạch tạo ra một loại tiền điện tử cực kỳ phổ biến hoặc một cái gì đó tương tự, thì họ có thể cần hàng trăm hoặc hàng nghìn bit cho mỗi ID. Nhưng ngay lập tức giả định rằng đó là những yêu cầu có thể khuyến khích nhiều công việc hơn mức cần thiết nếu một thư viện UUID tiêu chuẩn là đủ.
8bittree

@ 8bittree Nếu sử dụng các thư viện tiêu chuẩn là bất kỳ lợi thế nào, thì bằng mọi cách hãy tìm UUID. Nhưng việc rút một số byte ngẫu nhiên ra khỏi urandomcông việc không phải là nhiều hơn so với sử dụng thư viện UUID. Tôi chỉ thực hiện cả hai trong Python để so sánh và mỗi phương thức có chính xác 25 ký tự mã nguồn.
kasperd

3

Tôi sẽ gọi đây là thực hành xấu. Số ngẫu nhiên tạo ra đơn giản là không tạo số duy nhất, họ chỉ tạo số ngẫu nhiên. Một phân phối ngẫu nhiên có khả năng bao gồm một số trùng lặp. Bạn có thể làm cho tình huống này có thể chấp nhận được bằng cách thêm vào một yếu tố thời gian. Nếu bạn nhận được thời gian hiện tại từ đồng hồ hệ thống tính bằng mili giây. Một cái gì đó như thế này:

parseToInt(toString(System.currentTimeMillis()) + toString(Random.makeInt()))

Sẽ đi một chặng đường dài. Rõ ràng để thực sự đảm bảo tính độc đáo, bạn cần sử dụng UUID / GUID. Nhưng chúng có thể tốn kém để tạo ra, những điều trên có thể là đủ, vì khả năng trùng lặp duy nhất là nếu việc tạo ngẫu nhiên có một bản sao trong cùng một mili giây.


9
1ms có thể là một thời gian dài trong một số hệ thống.
quant_dev

7
Điều này thực sự không làm giảm cơ hội va chạm. Xác suất xảy ra va chạm sau số N chính xác bằng với giải pháp ban đầu của OP. Thủ thuật sử dụng thời gian hiện tại làm hạt giống thường được sử dụng khi gán khóa liên tục.
Cort Ammon

2
@Fresheyeball Tôi tự tin rằng nó không có hiệu lực, trừ khi Random.makeInt () không thực sự tạo ra phân phối đồng đều từ giá trị tối thiểu của số nguyên đến giá trị tối đa của số nguyên. Đối với mỗi giá trị trong quá khứ được tạo bởi hàm này, có một giá trị ngẫu nhiên từ makeInt, trong bước thời gian chính xác này, tạo ra giá trị đó, tạo ra xung đột. Vì tất cả các giá trị từ makeInt đều có thể được trang bị, xác suất va chạm chính xác bằng xác suất va chạm mà không cần thêm thời gian.
Cort Ammon

2
@CortAmmon điều này không sử dụng thời gian hiện tại như một hạt giống và nó chắc chắn sẽ tạo ra sự khác biệt miễn là những số N đó không được tạo ra trong cùng một mili giây, bởi vì hai số với các phần dấu thời gian khác nhau không bao giờ va chạm. Nếu bạn tưởng tượng ví dụ của câu trả lời khác về một gói mỗi giây có 50% khả năng va chạm trong vòng chưa đầy một ngày, thì gói này có 0% khả năng va chạm ở một gói mỗi giây, ít nhất là cho đến khi thời gian đó currentTimeMilliskết thúc.
hobbs

3
@hobbs Bạn quên đi tràn số nguyên. Bây giờ nếu khóa mà OP sử dụng là cấu trúc chứa 2 số nguyên, một chứa System.currentTimeMillisvà một chứa Random.makeInt(), thì xác suất xảy ra va chạm sẽ giảm đáng kể. Tuy nhiên, đó không phải là những gì mã trong ví dụ này làm. Cho bất kỳ thời gian trước và giá trị ngẫu nhiên và bất kỳ thời điểm hiện tại nào , xác suất va chạm là giống hệt với xác suất của hai số ngẫu nhiên va chạm ở vị trí đầu tiên.
Cort Ammon

3

Nó phụ thuộc vào cả xác suất thất bại và hậu quả của thất bại.

Tôi nhớ một cuộc tranh luận giữa những người làm phần mềm và phần cứng trong đó những người làm phần cứng cho rằng một thuật toán có xác suất sai kết quả nhỏ (giống như 1 thất bại trong 100 năm) là có thể chấp nhận được và mọi người nghĩ rằng phần mềm này là vô cảm. Hóa ra, những người làm phần cứng thường xuyên tính toán tỷ lệ thất bại dự kiến ​​và rất quen với ý tưởng rằng mọi thứ đôi khi sẽ đưa ra câu trả lời sai, ví dụ do sự xáo trộn do tia vũ trụ gây ra; họ thấy lạ là những người làm phần mềm mong đợi độ tin cậy 100%.


1

Chắc chắn, bạn có xác suất khá thấp của hai số nguyên 32 bit ngẫu nhiên là tuần tự nhưng điều đó không hoàn toàn không thể. Quyết định kỹ thuật phù hợp dựa trên hậu quả của các vụ va chạm sẽ là gì, ước tính khối lượng số bạn tạo ra, thời gian duy nhất được yêu cầu và điều gì xảy ra nếu người dùng độc hại bắt đầu cố gắng gây ra va chạm.


0

Có thể chấp nhận được khi cho rằng các số ngẫu nhiên sẽ là duy nhất nhưng bạn phải cẩn thận.

Giả sử các số ngẫu nhiên của bạn được phân phối bằng nhau, xác suất xảy ra va chạm là khoảng (n 2/2 ) / k trong đó n là số lượng ngẫu nhiên bạn tạo và k là số giá trị có thể có mà một số "ngẫu nhiên" có thể lấy.

Bạn không đặt một con số về mặt thiên văn không chắc chắn, vì vậy hãy lấy số đó là 1 trên 2 30 (khoảng một tỷ). Hãy nói thêm rằng bạn tạo 2 30 gói (nếu mỗi gói đại diện cho khoảng một kilobyte dữ liệu thì điều này có nghĩa là khoảng một terabyte tổng dữ liệu, lớn nhưng không thể tưởng tượng được). Chúng tôi thấy rằng chúng tôi cần một số ngẫu nhiên với ít nhất 2 89 giá trị có thể.

Đầu tiên số ngẫu nhiên của bạn cần phải đủ lớn. Một số ngẫu nhiên 32 bit có thể có tối đa 2 32 giá trị có thể. Đối với một máy chủ bận rộn mà không nơi nào đủ cao.

Thứ hai, trình tạo số ngẫu nhiên của bạn cần phải có trạng thái bên trong đủ lớn. Nếu trình tạo số ngẫu nhiên của bạn chỉ có trạng thái bên trong 32 bit thì cho dù giá trị bạn tạo ra từ nó lớn đến đâu, bạn vẫn sẽ chỉ nhận được tối đa 2 32 giá trị có thể.

Thứ ba, nếu bạn cần các số ngẫu nhiên là duy nhất trên các kết nối thay vì chỉ trong một kết nối, trình tạo số ngẫu nhiên của bạn cần phải được khởi tạo tốt. Điều này đặc biệt đúng nếu chương trình của bạn được khởi động lại thường xuyên.

Nói chung, các trình tạo số ngẫu nhiên "thông thường" trong các ngôn ngữ lập trình không phù hợp cho việc sử dụng đó. Các trình tạo số ngẫu nhiên được cung cấp bởi các thư viện mật mã thường là.


0

được xây dựng trong một số câu trả lời ở trên là giả định rằng trình tạo số ngẫu nhiên thực sự là 'phẳng' - rằng xác suất của hai số bất kỳ là số tiếp theo được tạo là như nhau.

Điều đó có lẽ không đúng với hầu hết các trình tạo số ngẫu nhiên. Hầu hết trong số đó sử dụng một số đa thức bậc cao liên tục được áp dụng cho một hạt giống.

Điều đó nói rằng, có nhiều hệ thống ngoài kia phụ thuộc vào sơ đồ này, thường là với UUID. Ví dụ: mọi đối tượng và tài sản trong Cuộc sống thứ hai đều có UUID 128 bit, được tạo ngẫu nhiên và chúng hiếm khi va chạm.


0

Rất nhiều người đã đưa ra câu trả lời chất lượng cao, nhưng tôi muốn thêm một vài điểm nhỏ: đầu tiên, quan điểm của @nomadictype về nghịch lý sinh nhật là tuyệt vời .

Một điểm khác: tính ngẫu nhiên không đơn giản để tạo và định nghĩa như mọi người có thể giả định thông thường. (Trong thực tế, có những thử nghiệm thống kê thực sự cho tính ngẫu nhiên có sẵn).

Như đã nói, điều quan trọng là phải biết về Ngụy biện của Gambler , đó là một ngụy biện thống kê nơi mọi người cho rằng các sự kiện độc lập bằng cách nào đó ảnh hưởng lẫn nhau. Các sự kiện ngẫu nhiên thường độc lập thống kê với nhau - nghĩa là nếu bạn tạo ngẫu nhiên "10" thì điều đó sẽ không thay đổi xác suất trong tương lai của bạn tạo ra nhiều "10" nhất. (Có lẽ ai đó có thể đưa ra một ngoại lệ cho quy tắc đó, nhưng tôi hy vọng rằng đó sẽ là trường hợp đối với hầu hết các trình tạo số ngẫu nhiên).

Vì vậy, câu trả lời của tôi là nếu bạn có thể giả sử rằng một chuỗi số ngẫu nhiên đủ dài là duy nhất, thì chúng sẽ không thực sự là số ngẫu nhiên vì đó sẽ là một mẫu thống kê rõ ràng. Ngoài ra, điều đó có nghĩa là mỗi số mới không phải là một sự kiện độc lập bởi vì nếu bạn tạo, chẳng hạn, số 10 có nghĩa là xác suất tạo ra bất kỳ số 10 nào trong tương lai sẽ là 0% (điều đó không thể xảy ra), cộng với điều đó có nghĩa là bạn sẽ tăng tỷ lệ nhận được một số khác hơn 10 (tức là bạn càng tạo ra nhiều số, xác suất của mỗi số còn lại càng cao).

Một điều nữa cần xem xét: cơ hội chiến thắng Powerball khi chơi một trò chơi là, theo tôi hiểu, khoảng 1 trên 175 triệu. Tuy nhiên, tỷ lệ người chiến thắng cao hơn đáng kể. Bạn quan tâm nhiều hơn đến tỷ lệ cược của một người nào đó "chiến thắng" (nghĩa là trùng lặp) so với tỷ lệ cược của bất kỳ con số cụ thể nào "chiến thắng" / là một bản sao.


Nếu một người đang tạo ra các mã định danh 4096 bit theo cách mà mọi bit đều có khả năng bằng 0 hoặc 1 độc lập với bất kỳ bit nào khác được tạo trong cùng hoặc bất kỳ mã định danh nào khác, thì xác suất mà bất kỳ hai mã định danh nào cũng khớp trở nên nhỏ bé ngay cả khi người ta ngẫu nhiên tạo ra một định danh khác nhau cho mỗi nguyên tử khoảng 4.0E81 trong vũ trụ quan sát được. Thực tế là các số nhận dạng như vậy gần như chắc chắn sẽ là duy nhất sẽ không khiến chúng trở thành "không ngẫu nhiên"
supercat

@supercat Điều đó đúng - với một số lượng đủ lớn, rất khó có khả năng sẽ có các bản sao, nhưng điều đó không phải là không thể. Nó thực sự phụ thuộc vào mức độ tồi tệ của hậu quả của tính không độc đáo là liệu những gì OP mô tả là một ý tưởng tốt.
EJoshuaS - Phục hồi Monica

Nếu xác suất xảy ra va chạm ngẫu nhiên nhỏ hơn xác suất xảy ra vụ thiên thạch phá hủy các thiết bị dựa trên các id duy nhất, từ góc độ kỹ thuật thì không cần phải lo lắng về điều trước. Sẽ có một nhu cầu lớn phải lo lắng về bất cứ điều gì có thể khiến các số ngẫu nhiên không độc lập, nhưng va chạm ngẫu nhiên sẽ không thành vấn đề.
supercat

@supercat Tôi nghĩ rằng bạn đang đọc sai điều này, hãy xem câu trả lời khác về nghịch lý sinh nhật, tôi nghĩ rằng một sự va chạm có thể xảy ra hơn nhiều so với tính toán của bạn - OP chỉ sử dụng số 32 bit nên tôi không chắc bạn ở đâu ' nhận được 4096 từ, và như những người du mục cho thấy khả năng xảy ra va chạm cuối cùng với một số độ dài đó thực sự cao đáng ngạc nhiên.
EJoshuaS - Phục hồi Monica

Bạn đúng rằng một số 32 bit là quá ngắn ngay cả đối với các quần thể nhỏ nếu va chạm hoàn toàn không thể chấp nhận được. Nếu một người sử dụng một số đủ lớn, người ta có thể giảm xác suất va chạm ngẫu nhiên đến mức người ta có thể cho rằng họ chỉ không xảy ra một cách an toàn, và trong nhiều trường hợp sử dụng số lớn hơn có thể tốt hơn là cố gắng sử dụng các phương tiện khác đảm bảo tính duy nhất, vì cái sau thường yêu cầu có quyền truy cập vào các chuyển đổi trạng thái không thể hoàn tác hoặc khôi phục, ngay cả khi đồng hồ của hệ thống được đặt lại hoặc hệ thống được tải lại từ bản sao lưu.
supercat

0

Không quan trọng bạn sử dụng bao nhiêu bit - bạn KHÔNG THỂ đảm bảo rằng hai số "ngẫu nhiên" sẽ khác nhau. Thay vào đó, tôi khuyên bạn nên sử dụng một cái gì đó như địa chỉ IP hoặc địa chỉ mạng khác của máy tính và số thứ tự, tốt nhất là số thứ tự LỚN của HONKIN - 128 bit (rõ ràng không dấu) nghe có vẻ tốt, nhưng 256 sẽ tốt hơn.


-1

Tất nhiên là không rồi. Trừ khi rng bạn đang sử dụng các mẫu mà không thay thế, sẽ có cơ hội - dù là nhỏ - trùng lặp.

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.