Короткий URL с уникальной, но случайно сгенерированной строкой? - PullRequest
0 голосов
/ 28 апреля 2020

Итак, я пытался создать URL-адрес, но я не могу сгенерировать уникальные, но random строки. Я везде искал решение, но не смог найти его, поэтому разместил его здесь.

У меня есть таблица, в которой я получаю автоматически сгенерированный последовательный первичный ключ (ID) для вставленной записи. Теперь я беру этот идентификатор и запускаю на нем биективную функцию, которая обращается

0  → a
1  → b
...
25 → z
...
52 → 0
61 → 9

Теперь проблема в том, что сгенерированная строка не случайна. Например:

63 --> b1
64 --> b2
...
1836 --> bpa
1836 --> bpb

Что очень предположительно. Я даже пытался закодировать идентификатор в Base64, но результирующая строка снова является предположительной, и если я вместо этого использую GUID и кодирую ее в Base64, результирующая строка будет очень большой. Максимальная строка должна состоять из 7,8 символов - в идеале 3,4 символа.

Мне интересно, как bit.ly это делает? сгенерированный ими короткий URL всегда уникален и случайен.

Ответы [ 2 ]

2 голосов
/ 28 апреля 2020

Преобразовать последовательные целые числа в непоследовательные 4-символьные токены довольно просто. Если вы используете обратимый алгоритм, вы также можете легко преобразовать эти токены обратно в последовательные целые числа, которые можно использовать для получения URL-адресов из базы данных.

Примечание: Если вы ' Если вы планируете открыть общедоступную службу сокращения URL-адресов c, то токены всего из 4 символов алфавита c могут быть довольно быстро исчерпаны. Но для личного или корпоративного сайта они должны быть более чем адекватными. Описанный ниже метод также будет работать с более длинными токенами, но если вам действительно нужна система, которая может хранить миллиарды (или триллионы) URL-адресов, вам нужно будет более тщательно продумать, как вы собираетесь организовать все эти данные.

A линейный конгруэнтный генератор - хороший способ запутывания чисел. Если вы работаете с диапазоном от 0 до 62 4 -1, то, очевидно, ваш модуль m будет равен 62 4 . А так как простые множители m равны 2 и 31, множитель a должен быть на единицу больше, чем кратное 124 (, как описано здесь ). Значение c может быть любым ненулевым значением, относительно простым относительно m . Например:

function lcg($n) {
    # (10345073 - 1) % 124 == 0
    $m = 14776336; # = 62**4
    $a = 10345073;
    $c = 8912423;
    $n = ($n * $a + $c) % $m;
    return $n;
}

Обратная функция довольно похожа. Вместо a он использует модульный мультипликативный обратный (мод m ), а вместо c он использует m - c:

function lcg_inv($n) {
    # (10345073 * 5661345) % (62**4) == 1
    $m = 14776336; # = 62**4
    $a_ = 5661345;
    $c_ = 5863913; # = $m-8912423
    $n = (($n + $c_) * $a_) % $m;
    return $n;
}

Поскольку LCG довольно легко предсказать из нескольких выходных значений, вы можете добавить еще один слой запутывания путем рандомизации порядка символов, используемых для представления этих чисел в базе 62 (например, W3qVL... вместо abcde...)

function int_2_token($n) {
    $alf = 'W3qVLpEKDxn8vzG0SQPfIX2yO51JsHBYCRbouTatZ4hMdlmF67UcNiAgwke9jr';
    $tok = '';
    if ($n < 0 || $n >= 62**4) return ''; # Value out of range
    $n = lcg($n);
    for ($i=0; $i<4; $i++) {
        $r = $n % 62;
        $tok .= $alf[$r];
        $n = ($n - $r) / 62;
    }
    return $tok;
}

function token_2_int($tok) {
    $t = [ '0'=>15, '1'=>26, '2'=>22, '3'=>1,  '4'=>41, '5'=>25, '6'=>48, '7'=>49,
           '8'=>11, '9'=>59, 'A'=>54, 'B'=>30, 'C'=>32, 'D'=>8,  'E'=>6,  'F'=>47,
           'G'=>14, 'H'=>29, 'I'=>20, 'J'=>27, 'K'=>7,  'L'=>4,  'M'=>43, 'N'=>52,
           'O'=>24, 'P'=>18, 'Q'=>17, 'R'=>33, 'S'=>16, 'T'=>37, 'U'=>50, 'V'=>3, 
           'W'=>0,  'X'=>21, 'Y'=>31, 'Z'=>40, 'a'=>38, 'b'=>34, 'c'=>51, 'd'=>44,
           'e'=>58, 'f'=>19, 'g'=>55, 'h'=>42, 'i'=>53, 'j'=>60, 'k'=>57, 'l'=>45,
           'm'=>46, 'n'=>10, 'o'=>35, 'p'=>5,  'q'=>2,  'r'=>61, 's'=>28, 't'=>39,
           'u'=>36, 'v'=>12, 'w'=>56, 'x'=>9,  'y'=>23, 'z'=>13 ];
    $n = 0;
    if (!preg_match('/^[a-z0-9]{4}$/i', $tok)) return -1; # Invalid token
    for ($i=3; $i>=0; --$i) {
        $n = $n * 62 + $t[$tok[$i]];
    }
    return lcg_inv($n);
}

Поэтому, когда вы получаете новый URL-адрес для сокращения, вставьте его в свою базу данных с помощью автоматического увеличивая значение идентификатора, и передайте это значение идентификатора int_2_token(), чтобы получить четырехзначный токен для использования в сокращенном URL-адресе. Когда запрашивается этот сокращенный URL-адрес, передайте токен на token_2_int(), чтобы восстановить этот идентификатор, чтобы вы могли получить исходный URL-адрес.


Примечание: Не забывайте, что набор из всех четырехсимвольных токенов включает в себя весь набор всех четырехбуквенных слов . Возможно, вы захотите убедиться, что ваш URL-адрес не выдает ничего вульгарного или оскорбительного.

2 голосов
/ 28 апреля 2020

Что вы делаете, это генерировать последовательные ключи. Первый URL-адрес - 1, следующий - 2 и т. Д. c. Но вы запутываете эти ключи, однозначно сопоставляя их каждому номеру. Таким образом, 1 может стать 76839427, а 2 может стать 9935. Затем вы base64 кодируете число.

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

Я приведу пример сопоставления в Эффективный алгоритм генерации уникальных (неповторяющихся) случайных чисел .

Другая возможность - использовать регистр сдвига с линейной обратной связью с длительным периодом. Вы можете создать один с периодом 2 ^ 64. Гарантируется, что не повторится, пока вы не сгенерируете 2 ^ 64 числа.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...