Я внедряю сетевой протокол, и мне требуется, чтобы пакеты имели уникальные идентификаторы. До сих пор я только что генерировал случайные 32-разрядные целые числа и предполагал, что астрономически маловероятно, что в течение срока службы программы / соединения произойдет столкновение. Это вообще считается приемлемой практикой в рабочем коде, или следует разработать более сложную систему для предотвращения коллизий?
programming-practices
Феникс
источник
источник
Ответы:
Остерегайтесь парадокса дня рождения .
Предположим, вы генерируете последовательность случайных значений (равномерно, независимо) из набора размера N (N = 2 ^ 32 в вашем случае).
Затем практическое правило для парадокса дня рождения гласит, что после того, как вы сгенерировали значения sqrt (N), существует как минимум 50% вероятность того, что произошло столкновение, то есть, что в сгенерированная последовательность.
Для N = 2 ^ 32, sqrt (N) = 2 ^ 16 = 65536. Таким образом, после того, как вы сгенерировали около 65 000 идентификаторов, более вероятно, что два из них столкнутся, чем нет! Если вы генерируете идентификатор в секунду, это произойдет менее чем за день; Излишне говорить, что многие сетевые протоколы работают намного быстрее, чем это.
источник
Широко считается приемлемым полагаться на то, что случайные числа являются уникальными, если эти числа имеют достаточно битов. Существуют криптографические протоколы, в которых повторение случайного числа нарушит всю безопасность. И пока в используемом генераторе случайных чисел нет серьезных уязвимостей, это не было проблемой.
Один из алгоритмов для генерации UUID будет эффективно генерировать ID, состоящий из 122 случайных битов, и предполагать, что он будет уникальным. И два других алгоритма основаны на уникальном хэш-значении, усеченном до 122 битов, что имеет примерно такой же риск коллизий.
Таким образом, существуют стандарты, основанные на том, что 122 битов достаточно для того, чтобы случайный идентификатор был уникальным, но 32 битов явно недостаточно. С 32-битными идентификаторами требуется только около 2¹⁶ идентификаторов, прежде чем риск коллизии достигнет 50%, потому что с 2¹⁶ идентификаторами будет близко к 2¹3 парам, каждая из которых может быть коллизией.
Даже 122 бита меньше, чем я рекомендовал бы в любом новом дизайне. Если для вас важно придерживаться некоторой стандартизации, используйте UUID. В противном случае используйте нечто большее, чем 122 бита.
Хеш-функция SHA1 с выходом 160 бит больше не считается безопасной, что частично связано с тем, что 160 бит недостаточно для обеспечения уникальности выходов. Современные хеш-функции имеют выходы от 224 до 512 бит. Случайно сгенерированные идентификаторы должны стремиться к одинаковым размерам, чтобы обеспечить уникальность с хорошим запасом прочности.
источник
sqrt(2^122)
= 2,3 квадриллиона квадриллиона UUIDurandom
не сложнее, чем использовать библиотеку UUID. Я просто реализовал оба в Python для сравнения, и каждый метод был ровно 25 символов исходного кода.Я бы назвал это плохой практикой. Генераторы случайных чисел просто не создают уникальные числа, они просто создают случайные числа. Случайное распределение может включать несколько дубликатов. Вы можете сделать это обстоятельство неприемлемо маловероятным, добавив элемент времени. Если вы получите текущее время из системных часов в миллисекундах. Что-то вроде этого:
Пройдет долгий путь. Очевидно, чтобы действительно гарантировать уникальность, вам нужно использовать UUID / GUID. Но они могут быть дорогими для генерации, вышеупомянутого, вероятно, достаточно, так как единственная возможность перекрытия, если случайная генерация имела дубликат в ту же миллисекунду.
источник
currentTimeMillis
наступит время.System.currentTimeMillis
а другое содержитRandom.makeInt()
, тогда вероятность столкновения существенно снижается. Однако это не то, что делает код в этом примере. Учитывая любое предыдущее время и случайное значение, а также любое текущее время, вероятность столкновения идентична вероятности столкновения двух случайных чисел в первую очередь.Это зависит как от вероятности отказа, так и от последствий отказа.
Я помню дебаты между специалистами по программному обеспечению и аппаратному обеспечению, когда специалисты по аппаратному обеспечению считали, что алгоритм с небольшой вероятностью ошибочных результатов (что-то вроде 1 отказа за 100 лет) был приемлемым, а специалисты по программному обеспечению считали это анафемой. Оказалось, что аппаратные специалисты обычно рассчитывали ожидаемые частоты отказов и очень привыкли к мысли, что иногда все будет давать неправильные ответы, например, из-за помех, вызванных космическими лучами; им показалось странным, что программисты ожидали 100% надежности.
источник
Конечно, у вас есть довольно низкая вероятность того, что два случайных 32-разрядных целых числа будут последовательными, но это не совсем невозможно. Соответствующее инженерное решение основано на последствиях коллизий, оценке объема генерируемых вами чисел, времени жизни, в течение которого требуется уникальность, и что произойдет, если злонамеренный пользователь попытается вызвать коллизии.
источник
Можно предположить, что случайные числа будут уникальными, но вы должны быть осторожны.
Предполагая, что ваши случайные числа распределены поровну, вероятность столкновения примерно равна (n 2/2 ) / k, где n - количество генерируемых вами случайных чисел, а k - количество возможных значений, которые может принять «случайное» число.
Вы не ставите число на астрономически маловероятное, поэтому давайте возьмем его как 1 к 2 30 (примерно на миллиард). Допустим также, что вы генерируете 2 30 пакетов (если каждый пакет представляет около килобайта данных, то это означает терабайт общих данных, большой, но невообразимо большой). Мы находим, что нам нужно случайное число с по крайней мере 2 89 возможных значений.
Во-первых, ваши случайные числа должны быть достаточно большими. 32-битное случайное число может иметь не более 2 32 возможных значений. Для занятого сервера, который далеко не достаточно высок.
Во-вторых, ваш генератор случайных чисел должен иметь достаточно большое внутреннее состояние. Если ваш генератор случайных чисел имеет только 32-битное внутреннее состояние, то независимо от того, насколько велико значение, которое вы сгенерируете из него, вы все равно получите не более 2 32 возможных значений.
В-третьих, если вам нужно, чтобы случайные числа были уникальными для разных соединений, а не только внутри соединения, ваш генератор случайных чисел должен быть хорошо виден. Это особенно верно, если ваша программа часто перезапускается.
В целом, «обычные» генераторы случайных чисел в языках программирования не подходят для такого использования. Генераторы случайных чисел, предоставляемые криптографическими библиотеками, обычно являются.
источник
в некоторые из приведенных выше ответов заложено предположение, что генератор случайных чисел действительно «плоский» - вероятность того, что любые два числа будут сгенерированы следующим, одинакова.
Это, вероятно, не так для большинства генераторов случайных чисел. Большинство из которых используют некоторый многочлен высокого порядка, неоднократно примененный к семени.
Тем не менее, существует много систем, которые зависят от этой схемы, обычно с UUID. Например, каждый объект и ресурс в Second Life имеет 128-битный UUID, генерируемый случайным образом, и они редко сталкиваются.
источник
Многие люди уже дали качественные ответы, но я хотел бы добавить несколько незначительных моментов: во-первых, замечание @nomadictype о парадоксе дня рождения превосходно .
Еще один момент: случайность не так проста, чтобы генерировать и определять, как люди могут себе представить. (На самом деле, существуют статистические тесты на случайность ).
С учетом сказанного важно осознавать заблуждение игрока , которое является статистической ошибкой, когда люди предполагают, что независимые события как-то влияют друг на друга. Случайные события, как правило, статистически независимы друг от друга, т. Е. Если вы случайным образом сгенерируете «10», это не изменит вашу будущую вероятность генерировать больше «10» в наименьшей степени. (Возможно, кто-то может придумать исключение из этого правила, но я ожидаю, что это будет иметь место практически для всех генераторов случайных чисел).
Поэтому мой ответ таков: если бы вы могли предположить, что достаточно длинная последовательность случайных чисел была уникальной, они не были бы действительно случайными числами, потому что это была бы четкая статистическая схема. Кроме того, это будет означать, что каждое новое число не является независимым событием, потому что, если вы сгенерируете, например, 10, это будет означать, что вероятность создания любых будущих 10-х будет 0% (это не может произойти), плюс это будет означать, что вы увеличите шансы получить число, отличное от 10 (то есть, чем больше чисел вы сгенерируете, тем выше вероятность каждого из оставшихся чисел).
Еще один момент, на который следует обратить внимание: шанс выиграть Powerball от участия в одной игре, насколько я понимаю, составляет примерно 1 на 175 миллионов. Однако шансы на победу у кого-то значительно выше. Вас больше интересуют шансы того, что кто-то "выиграет" (т.е. будет дубликатом), чем шансы того или иного конкретного числа "выиграть" / быть дубликатом.
источник
Неважно, сколько битов вы используете - вы НЕ МОЖЕТЕ гарантировать, что два «случайных» числа будут разными. Вместо этого я предлагаю вам использовать что-то вроде IP-адреса или другого сетевого адреса компьютера и порядковый номер, предпочтительно БОЛЬШОЙ последовательный номер HONKIN '- 128 бит (очевидно, без знака) звучат как хорошее начало, но 256 будет лучше.
источник
Нет, конечно нет. Если только вы не используете сэмплы без замены, есть шанс - хотя и малый - дублирования.
источник