Как создать уникальную случайную последовательность символов в C#?

я реализую функцию сокращения URL в моем приложении, чтобы предоставить моим пользователям более короткие альтернативные URL, которые могут быть использованы в Twitter. Дело в том, чтобы быть независимым от служб сокращения, которые предлагают эту же услугу и включают ее в качестве функции моего веб-приложения.

каков наилучший способ создать уникальную случайную последовательность символов около 6 символов? Я планирую использовать это как индекс для элементов в моей базе данных, которые будут иметь альтернативу URL-адреса.

редактировать:

эта функция будет использоваться на веб-сайте job board, где каждое новое объявление о работе получит пользовательский URL с названием Плюс более короткий, который будет использоваться в Twitter. Тем не менее, общее количество уникальных комбинаций 6 символов будет более чем достаточно в течение длительного времени.

10 ответов


вам действительно нужен "случайный", или "уникальный" будет достаточно?

Unique чрезвычайно прост - Просто вставьте URL-адрес в базу данных и преобразуйте последовательный id для этой записи в базовый номер n, который представлен выбранным набором символов.

например, если вы хотите использовать только [A-Z] в своей последовательности, вы преобразуете идентификатор записи в базовое число 26, где A=1, B=2,... Z=26. В algothithm является рекурсивной div26/mod26, где частное представляет необходимый символ и остаток используется для вычисления следующего символа.

затем при получении URL-адреса вы выполняете обратную функцию, которая заключается в преобразовании числа base-26 обратно в decimal. Выполните выбор URL, где ID = decimal, и все готово!

изменить:

private string alphabet = "abcdefghijklmnopqrstuvwxyz"; 
   // or whatever you want.  Include more characters 
   // for more combinations and shorter URLs

public string Encode(int databaseId)
{
    string encodedValue = String.Empty;

    while (databaseId > encodingBase)
    {
        int remainder;
        encodedValue += alphabet[Math.DivRem(databaseId, alphabet.Length, 
            out remainder)-1].ToString();
        databaseId = remainder;
    }
    return encodedValue;
}

public int Decode(string code)
{
    int returnValue;

    for (int thisPosition = 0; thisPosition < code.Length; thisPosition++)
    {
        char thisCharacter = code[thisPosition];

        returnValue += alphabet.IndexOf(thisCharacter) * 
            Math.Pow(alphabet.Length, code.Length - thisPosition - 1);
    }
    return returnValue;
}

самый простой способ сделать уникальные последовательности - сделать это последовательно, то есть: aaaaaa aaaaab aaaaac ... Они не обязательно самые красивые, но гарантируют уникальность для первых 12230590463 последовательностей (при условии, что вы использовали a-z и A-Z в качестве уникальных символов). Если вам нужно больше URL-адресов, Вам нужно добавить седьмой символ.

Они не являются случайными последовательностями. Если вы делаете случайные, просто выберите случайный символ 48, 6 раз. Вам нужно проверить ваше существующее БД для" используемых " последовательностей, однако, поскольку вы с большей вероятностью получите столкновения.


Я бы использовал систему autonumber и создал алгоритм для генерации ключей. ie 1 = a, 2 = b, 27 = aa etc.

вы можете использовать autonumber базы данных, чтобы гарантировать, что Ваш URL уникален, и вы можете рассчитать URL-адрес, возможно, в sproc в БД или на вашем бизнес-уровне?

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


полезность случайного генератора ограничивается предотвращением пользователей от подключения случайных URL-адресов, чтобы найти то, на что они не должны иметь ссылки. Если это не ваша цель, то последовательные идентификаторы должны работать нормально. Если вы просто не хотите создавать у пользователей впечатление, что они используют "детскую" технологию (когда они видят, что их объявление о работе #000001), почему бы не начать последовательность с некоторого произвольного значения?


когда вы заявляете "общее количество уникальных 6 комбинаций символов будет более чем достаточно в течение длительного времени " для вашего случайного поколения вы учитывали парадокс дней рождения в ваши расчеты? Это, как правило, проклятие любой попытки создать случайные идентификаторы в диапазоне, который составляет только 1 порядок величины или меньше ожидаемого диапазона, который будет необходим.

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

Я бы предложил вам пойти с идеей кодирования последовательные идентификаторы. Чтобы избежать проблемы пользователей, способных просто увеличивать / уменьшать значение в URL-адресе для "изучения", вы можете использовать комбинацию битового сдвига и альтернативный упорядоченный список букв (вместо 1=a, 2=b используйте 1=t, 2=j и т. д.).


думать об этом подробнее вот идея.

вы можете начать с ключевой таблицы, увеличивая символы AAAAAA-ZZZZZZ.

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

мысли?

для случайного выбора попробуйте это ссылке

Select a random row with MySQL:

SELECT column FROM table
ORDER BY RAND()
LIMIT 1
Select a random row with PostgreSQL:

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1
Select a random row with Microsoft SQL Server:

SELECT TOP 1 column FROM table
ORDER BY NEWID()
Select a random row with IBM DB2

SELECT column, RAND() as IDX 
FROM table 
ORDER BY IDX FETCH FIRST 1 ROWS ONLY
Thanks Tim

Select a random record with Oracle:

SELECT column FROM
( SELECT column FROM table
ORDER BY dbms_random.value )
WHERE rownum = 1


вместо того, чтобы хранить таблицу всех возможных значений, просто сохраните таблицу значений, которые вы использовали. Используйте функцию random для генерации 6 случайных значений, от 1 до 26, сделайте строку из этого и сохраните ее в массиве или таблице. Если он уже существует, вы можете (a) создать другую строку или (b) перейти через таблицу к следующей доступной (отсутствующей) 6-буквенной строке и использовать это значение. b) будет более эффективным по мере заполнения таблицы.


следуя идее ответа Рида Копси, я представляю следующий код:

class IDGetter
{
    private StringID ID = new StringID();
    public string GetCurrentID()
    {
        string retStr = "";
        if (ID.char1 > 51)
            id.char1 = 0;
        if (ID.char2 > 51)
            id.char2 = 0;
        if (ID.char3 > 51)
            id.char3 = 0;
        if (ID.char4 > 51)
            id.char4 = 0;
        if (ID.char5 > 51)
            id.char5 = 0;
        if (ID.char6 > 51)
            throw new Exception("the maximum number of id's has been reached");
        return ToIDChar(ID.char1) + ToIDChar(ID.char2) + ToIDChar(ID.char3) + ToIDChar(ID.char4) + ToIDChar(ID.char5) + ToIDChar(ID.char6)
        id.char1++;
    }
    public void SetCurrentID(StringID id) //for setting the current ID from storage or resetting it or something
    {
        this.ID = id;
    }
    private const string alphabet = "abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static string ToIDChar(int number)
    {
        if (number > 51 || number < 0)
        {
            throw new InvalidArgumentException("the number passed in (" + number + ") must be between the range 0-51");
        }
        return alphabet[number];
    }
}
public struct StringID 
{
    public int char1 = 0;
    public int char2 = 0;
    public int char3 = 0;
    public int char4 = 0;
    public int char5 = 0;
    public int char6 = 0;
}

возможно, вы захотите придумать способ хранения текущего кода, но это должно работать.


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

/// Generates a string and checks for existance
/// <returns>Non-existant string as ID</returns>
public static string GetRandomNumbers(int numChars, string Type)
{
    string result = string.Empty;
    bool isUnique = false;
    while (!isUnique)
    {
        //Build the string
        result = MakeID(numChars);
        //Check if unsued
        isUnique = GetValueExists(result, Type);
    }
    return result;
}
/// Builds the string
 public static string MakeID(int numChars)
{
    string random = string.Empty;
    string[] chars = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };
    Random rnd = new Random();
    for (int i = 0; i < numChars; i++)
    {
        random += chars[rnd.Next(0, 35)];
    }
    return random;
}
/// Checks database tables based on type for existance, if exists then retry
/// <returns>true or false</returns>
private static bool GetValueExists(string value, string Type)
{
    bool result = false;
    string sql = "";
    if (Type == "URL")
    {
        sql = string.Format(@"IF EXISTS (SELECT COUNT(1) FROM myTable WHERE uniqueString = '{0}')
         BEGIN
             SELECT 1
         END
          ELSE
          BEGIN
             SELECT 0
         END ", value);
    }
    //query the DB to see if it's in use
    result = //ExecuteSQL
    return result;
}