Мы планируем создавать промокоды для отправки клиентам, нам сказали, что каждый отправленный код ДОЛЖЕН БЫТЬ УНИКАЛЬНЫМ - 5 символов - буквенно-цифровые.

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

Может ли кто-нибудь дать мне какие-либо указания по созданию этой уникальной 5-символьной буквенно-цифровой строки, уникальной КАЖДЫЙ РАЗ?

3
Richard.Gale 14 Апр 2016 в 17:16

4 ответа

Лучший ответ

Как я уже упоминал в комментариях к другому моему ответу, этого может быть недостаточно для ваших целей. Я разработал еще код, который генерирует строку случайных буквенно-цифровых символов. На этот раз они не ограничиваются числами 0–9 и AF, т.е. шестнадцатеричными эквивалентами случайно сгенерированных полубайтов.. Вместо этого они состоят из полного диапазона буквенно-цифровых символов, по крайней мере, из прописных букв. Это должно значительно увеличить потенциал уникальности, учитывая, что мы переходим от 16 возможных символов с шестнадцатеричным числом до 36 возможных символов с полным алфавитом и 0-9.

Тем не менее, когда я провел более 10 000 000 попыток, было много неудач. Такова природа зверя: вероятность того, что у вас получатся дупсы с такой короткой струной, довольно высока. Во всяком случае, вот оно. Вы можете поиграть с этим. Если ваш клиент не против строчных букв - например, если «РОРЯП» отличается от «РориАп» - то это еще больше повысит вероятность уникальности.

/// <summary>
/// Instances of this class are used to geneate alpha-numeric strings.
/// </summary>
public sealed class AlphaNumericStringGenerator
{
    /// <summary>
    /// The synchronization lock.
    /// </summary>
    private object _lock = new object();

    /// <summary>
    /// The cryptographically-strong random number generator.
    /// </summary>
    private RNGCryptoServiceProvider _crypto = new RNGCryptoServiceProvider();

    /// <summary>
    /// Construct a new instance of this class.
    /// </summary>
    public AlphaNumericStringGenerator()
    {
        //Nothing to do here.
    }

    /// <summary>
    /// Return a string of the provided length comprised of only uppercase alpha-numeric characters each of which are
    /// selected randomly.
    /// </summary>
    /// <param name="ofLength">The length of the string which will be returned.</param>
    /// <returns>Return a string of the provided length comprised of only uppercase alpha-numeric characters each of which are
    /// selected randomly.</returns>
    public string GetRandomUppercaseAlphaNumericValue(int ofLength)
    {
        lock (_lock)
        {
            var builder = new StringBuilder();

            for (int i = 1; i <= ofLength; i++)
            {
                builder.Append(GetRandomUppercaseAphanumericCharacter());
            }

            return builder.ToString();
        }
    }

    /// <summary>
    /// Return a randomly-generated uppercase alpha-numeric character (A-Z or 0-9).
    /// </summary>
    /// <returns>Return a randomly-generated uppercase alpha-numeric character (A-Z or 0-9).</returns>
    private char GetRandomUppercaseAphanumericCharacter()
    {
            var possibleAlphaNumericValues =
                new char[]{'A','B','C','D','E','F','G','H','I','J','K','L',
                'M','N','O','P','Q','R','S','T','U','V','W','X','Y',
                'Z','0','1','2','3','4','5','6','7','8','9'};

            return possibleAlphaNumericValues[GetRandomInteger(0, possibleAlphaNumericValues.Length - 1)];
    }

    /// <summary>
    /// Return a random integer between a lower bound and an upper bound.
    /// </summary>
    /// <param name="lowerBound">The lower-bound of the random integer that will be returned.</param>
    /// <param name="upperBound">The upper-bound of the random integer that will be returned.</param>
    /// <returns> Return a random integer between a lower bound and an upper bound.</returns>
    private int GetRandomInteger(int lowerBound, int upperBound)
    {
        uint scale = uint.MaxValue;

        // we never want the value to exceed the maximum for a uint, 
        // so loop this until something less than max is found.
        while (scale == uint.MaxValue)
        {
            byte[] fourBytes = new byte[4];
            _crypto.GetBytes(fourBytes); // Get four random bytes.
            scale = BitConverter.ToUInt32(fourBytes, 0); // Convert that into an uint.
        }

        var scaledPercentageOfMax = (scale / (double) uint.MaxValue); // get a value which is the percentage value where scale lies between a uint's min (0) and max value.
        var range = upperBound - lowerBound;
        var scaledRange = range * scaledPercentageOfMax; // scale the range based on the percentage value
        return (int) (lowerBound + scaledRange);
    }
}
5
rory.ap 14 Апр 2016 в 16:39

Суть в том, что вам, вероятно, следует вернуться к руководству и сказать им, что требование обеспечить 100% абсолютную уникальность является очень дорогостоящим запросом и что вы можете получить 99,9999% уникальности за небольшую часть стоимости. Затем используйте код roryap для генерации случайного, в основном уникального кода.

0
Scottie 14 Апр 2016 в 14:32

Я бы создал таблицу (промокод) со всеми возможными строками из 5 цифр. Затем создайте еще одну таблицу Campaign PromoCode с двумя полями:

Code varchar(5), CampaignId uniqueidentifier

Таким образом, вы можете отслеживать использованные для каждой кампании. Чтобы получить случайный неиспользованный промокод, используйте эту инструкцию:

select top 1 Code 
from PromoCode pc
    left join CampaignPromoCode cpc on cpc.Code = pc.Code
where cpc.CampaignId = 'campaign-id-goes-here'
  and cpc.Code is null
order by newid()
0
Dmitri Trofimov 14 Апр 2016 в 14:31

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

/// <summary>
/// Return a string of random hexadecimal values which is 6 characters long and relatively unique.
/// </summary>
/// <returns></returns>
/// <remarks>In testing, result was unique for at least 10,000,000 values obtained in a loop.</remarks>
public static string GetShortID()
{
    var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider();
    var bytes = new byte[5];
    crypto.GetBytes(bytes); // get an array of random bytes.      
    return BitConverter.ToString(bytes).Replace("-", string.Empty); // convert array to hex values.
}

Я понимаю, что ваше требование состоит в том, что он «должен» быть уникальным, но помните, что уникальность - это в лучшем случае относительное понятие. Даже наш старый друг GUID не действительно уникален:

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

Если я правильно помню, я обнаружил, что мой код не был на 100% уникальным с 5 символами на многих, многих итерациях (сотни тысяч или, возможно, несколько миллионов - я точно не помню), но при тестировании с 6, результат был уникальным для не менее 10 000 000 значений, полученных в цикле.

Вы можете протестировать его самостоятельно на длине 5 и определить, достаточно ли он уникален для ваших целей. Просто переключите 6 на 5, если хотите.

Приложение: Некоторые другие напомнили мне, что вам, возможно, нужно подумать о безопасности потоков. Вот модифицированный подход:

private static object _lock = new object();

/// <summary>
/// Return a string of random hexadecimal values which is 6 characters long and relatively unique.
/// </summary>
/// <returns></returns>
/// <remarks>In testing, result was unique for at least 10,000,000 values obtained in a loop.</remarks>
public static string GetShortID()
{
    lock(_lock)
    {
        var crypto = new System.Security.Cryptography.RNGCryptoServiceProvider();
        var bytes = new byte[5];
        crypto.GetBytes(bytes); // get an array of random bytes.      
        return BitConverter.ToString(bytes).Replace("-", string.Empty); // convert array to hex values.
    }
}
2
rory.ap 14 Апр 2016 в 14:41