Создание собственного UID в стиле Tinyurl - PullRequest
17 голосов
/ 10 октября 2008

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

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

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

Меня интересует частота столкновений, и если приведенный ниже код является реалистичной имитацией ожидаемой частоты столкновений, которую вы получите от генерации хеша. В среднем я получаю 40-50 столкновений на миллион, однако, учитывая, что идентификатор не будет генерироваться миллион раз за раз, но, вероятно, только около 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, однако Sklivvz также является ответом на 2-ю часть (альтернатива). Можно ли создать в базе данных собственный генератор уникальных идентификаторов, или это будет на стороне клиента (в первую очередь будет 2 чтения)?

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

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

Ответы [ 8 ]

31 голосов
/ 10 октября 2008

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

У вас будет таблица БД, такая как

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

и соответствующие URL будут:

http://example.com/1
http://example.com/2
...
http://example.com/2W
...
6 голосов
/ 10 октября 2008

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

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

5 голосов
/ 10 октября 2008

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

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

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

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

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

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

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

4 голосов
/ 10 октября 2008

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

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

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

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

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

EDIT:

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

0 голосов
/ 10 октября 2008

из википедии :

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

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

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

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

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

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

0 голосов
/ 10 октября 2008

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

0 голосов
/ 10 октября 2008

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

0 голосов
/ 10 октября 2008

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

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

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

Исправление

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

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

...