Создание собственного стиля Tinyurl uid

Я пишу небольшую статью о человеко-читаемых альтернативах Guids/UID, например, те, которые используются на TinyURL для хэшей URL (которые часто печатаются в журналах, поэтому они должны быть короткими). ​​

Простой uid, который я генерирую, - 6 символов: либо строчная буква (a-z), либо 0-9.

"По моим расчетам капитан", что 6 взаимоисключающих событий, хотя вычисление вероятности столкновения становится немного сложнее, чем P (A или B) = P (A) + P (B), поскольку, очевидно, оно включает номеров и из приведенного ниже кода, вы можете увидеть, что он работает независимо от того, использовать ли число или букву с помощью 50/50.

Меня интересует скорость столкновения, и если приведенный ниже код представляет собой реалистичное моделирование ожидаемой скорости столкновения, которую вы получите от создания хэша. В среднем я получаю 40-50 столкновений на миллион, однако в виду, что uid не будет генерироваться миллион раз одновременно, но, вероятно, только около 10-1000 раз в минуту.

Какова вероятность столкновения каждый раз, и может ли кто-нибудь предложить лучший способ сделать это?

static Random _random = new Random();

public static void main()
{
    // Size of the key, 6
    HashSet<string> set = new HashSet<string>();
    int clashes = 0;
    for (int n=0;n < 1000000;n++)
    {
        StringBuilder builder = new StringBuilder();

        for (int i =0;i < 7;i++)
        {
            if (_random.NextDouble() > 0.5)
            {
                builder.Append((char)_random.Next(97,123));
            }
            else
            {
                builder.Append(_random.Next(0,9).ToString());
            }
        }

        if (set.Contains(builder.ToString()))
        {
            clashes++;
            Console.WriteLine("clash: (" +n+ ")" +builder.ToString());
        }

        set.Add(builder.ToString());
        _random.Next();
        //Console.Write(builder.ToString());
    }

    Console.WriteLine("Clashes: " +clashes);
    Console.ReadLine();
}

UPDATE: Здесь результирующая статья из этого вопроса

Я действительно задал два вопроса, поэтому я обманывал. Ответ, который я получил, был rcar's, однако Sklivvz также является ответом на вторую часть (альтернативу). Можно ли создать уникальный уникальный генератор идентификаторов в базе данных, или это будет клиентская сторона (которая может быть записана в 2 раза)?

Общая идея, с которой я столкнулась, заключалась в использовании идентификаторов в базах данных или других магазинах, которые могут использоваться по телефону или печатным материалам, а не по гигантскому 16-байтовому указателю.

ОБНОВЛЕНИЕ 2: Я поставил формулу для двух взаимоисключающих событий выше, а не из двух независимых (поскольку получение "а" в первый раз не означает, что вы не можете получить ' второй раз). Должны были быть P (A и B) = P (A) x P (B)

Ответ 1

Вероятность столкновения с одним конкретным идентификатором:

p = ( 0.5 * ( (0.5*1/10) + (0.5*1/26) ) )^6

что составляет около 1,7 × 10 ^ -9.

Вероятность столкновения после генерации n идентификаторов равна 1-p ^ n, поэтому у вас будет примерно 0,17% вероятности столкновения для каждой новой вставки после того, как было добавлено 1 миллион ID, около 1,7% после 10 миллионов ID и около 16% после 100 миллионов.

1000 IDs/minute составляет около 43 миллионов в месяц, так как Sklivvz отметил, что использование некоторого возрастающего ID, вероятно, будет лучшим способом в этом случае.

EDIT:

Чтобы объяснить математику, он по существу переворачивает монету, а затем выбирает число или букву 6 раз. Там 0,5 вероятность того, что совпадение монет совпадает, а затем 50% времени там 1/10 вероятность совпадения и 50% вероятность 1/26 вероятность соответствия. Это происходит 6 раз независимо, поэтому вы умножаете эти вероятности вместе.

Ответ 2

Почему вы хотите использовать случайную функцию? Я всегда предполагал, что tinyurl использовал базовое представление 62 (0-9A-Za-z) последовательного Id. Нет столкновений, и URL-адреса всегда бывают как можно короче.

У вас будет таблица DB, например

Id  URL
 1  http://google.com
 2  ...
... ...
156 ...
... ...

и соответствующие URL-адреса будут следующими:

http://example.com/1
http://example.com/2
...
http://example.com/2W
...

Ответ 3

Посмотрите Парадокс дня рождения, это точная проблема, с которой вы столкнулись.

Вопрос: сколько людей вам нужно собраться в комнате, чтобы у вас была 50% вероятность того, что у двух человек будет одна и та же дата рождения? Ответ может вас удивить.

Ответ 4

Некоторое время назад я сделал именно это, и я последовал тому, как упоминал Скливвз. Вся логика была разработана с помощью хранимой процедуры SQL-сервера и нескольких UDF (пользовательских функций). Шагами были:

  • скажите, что вы хотите укоротить этот URL: Создайте свой собственный uid tinyurl style
  • Вставьте URL-адрес в таблицу
  • Получить значение @@для идентификатора последней вставки (числовой идентификатор)
  • Преобразуйте идентификатор в соответствующее буквенно-цифровое значение на основе "домена" букв и цифр (я действительно использовал этот набор: "0123456789abcdefghijklmnopqrstuvwxyz" )
  • Вернуть это значение обратно, что-то вроде 'cc0'

Конверсия была реализована через пару очень коротких UDF.

Два преобразования, называемые один за другим, возвращают "последовательные" значения, такие как:

select dbo.FX_CONV (123456) -- returns "1f5n"

select dbo.FX_CONV (123457) -- returns "1f5o"

Если вам интересно, я могу поделиться кодом UDF.

Ответ 5

Почему бы просто не использовать алгоритм хэширования? и использовать хэш URL?

Если вы используете случайные числа, вероятность того, что вы столкнетесь, потому что они неопределенны.

хэши arent proovably уникальны, но есть довольно хороший шанс, что хэш строки будет уникальным.

Коррекция

На самом деле, подождите, пока вы захотите, чтобы они были по-человечески читабельны... если вы поместили их в шестнадцатеричный, они технически по-человечески читаемы.

или вы можете использовать алгоритм, который преобразует хэш в человечно читаемую строку. если человечно читаемая строка является другим представлением хэша, она также должна быть "уникальной", как хэш, то есть базой 36 исходного хэша.

Ответ 6

Я бы сгенерировал случайное значение, представляющее данные, которые вы собираетесь хешировать, а затем хэш и проверьте класы, а не пытайтесь имитировать случайные вручную хэши. Это даст вам лучший показатель. И у вас будет больше случайности, потому что у вас будет больше шансов на рандомизацию (предполагая, что ваши хэшированные данные больше:)).

Ответ 7

Если вы используете 6 символов, a-z и 0-9, это всего 36 символов. Таким образом, число перестановок составляет 36 ^ 6, что равно 2176782336.. поэтому он должен только столкнуться 1/2176782336 раз.

Ответ 8

from wikipedia:

При печати меньшего количества символов GUID иногда кодируются в строку base64 или Ascii85. Base64-кодированный GUID состоит из 22-24 символов (в зависимости от дополнения), например:

7QDBkvCA1+B9K/U0vrQx1A
7QDBkvCA1+B9K/U0vrQx1A==

и кодировка Ascii85 дает только 20 символов, e. г:.

5:$Hj:Pf\4RLB9%kU\Lj 

Итак, если вы заинтересованы в уникальности, GUID с кодировкой base64 немного приближается к тому, что вы хотите, хотя его не 6 символов.

Лучше всего сначала работать в байтах, а затем перевести эти байты в шестнадцатеричные для отображения, а не работать с символами напрямую.