【问题标题】:Create a UNIQUE 5 character alphanumeric string创建一个唯一的 5 个字符的字母数字字符串
【发布时间】:2016-04-14 14:16:02
【问题描述】:

我们正在考虑创建促销代码以发送给客户,我们被告知发送的每个代码都必须是唯一的 - 5 个字符 - 字母数字。

我想对连接的字符串进行哈希处理并获取哈希的前 5 个字符,但很有可能相同的 5 个字符会一次又一次地出现。

任何人都可以给我任何关于创建这个唯一的 5 字符字母数字字符串的指示吗?每次都是唯一的?

【问题讨论】:

  • 制作一个文本文件,并在制作时将每个代码附加到其中,然后每次制作一个都继续生成它们,直到它是一个不在文件中的唯一代码
  • 您必须将现有的存储在某处并将每个创建的与所有这些进行比较
  • 另外,在着手创建问题之前,请确保您的问题确实有解决方案。如果您可以使用 26 个字符 + 10 个数字(已经有问题,因为人们可能会抱怨 1 并且 i 容易混淆,0 和 o 等等),这仍然会给您留下不超过 36^5 = 60 466 176 个代码。够了吗?一直吗?一个月?一个星期?有多少店铺、顾客、行动?您何时以及如何回收代码?
  • @Richard.Gale:是否可以生成一个连续值列表,比如 1,000,000 个,存储在表中,然后让系统随机选择其中一个并将其标记为“已使用”?每个下一个查询将被限制为那些未使用的,并获得一个新的随机数等。
  • @Cᴏʀʏ 为什么是随机的?为什么不只是迭代?

标签: c# string alphanumeric promotion-code


【解决方案1】:

正如我在其他答案的 cmets 中提到的,它可能不足以满足您的目的。我编写了更多代码来生成一串随机的字母数字字符。这一次,它们不限于 0-9 和 A-F - 即随机生成的 nibbles 的十六进制等价物。相反,它们由完整范围的字母数字字符组成,至少包含大写字母。鉴于我们将从 16 个可能的十六进制字符变为 36 个可能的字符(完整的字母表和 0-9),这应该足以增加唯一性的潜力。

不过,当我运行它超过 10,000,000 次尝试时,仍然有很多重复。这只是野兽的本性:你会用这么短的字符串被骗的可能性相当高。无论如何,它就在这里。你可以玩弄它。如果您的客户不介意小写字母 - 例如如果“RORYAP”与“RoryAp”不同——那将进一步增加唯一性的可能性。

/// <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);
    }
}

【讨论】:

    【解决方案2】:

    我不久前想出了这个,它可能会满足您的需求。

    /// <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.
    }
    

    我了解您的要求是它“必须”是唯一的,但请记住,唯一性充其量只是一个相对概念。甚至我们的老朋友the 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.
        }
    }
    

    【讨论】:

    • 谢谢@roryap。我正在寻找一种基于代码的解决方案,它不涉及大量循环和/或数据库调用。但是是的,我需要每次都能保证唯一性,否则练习毫无意义
    • @Richard.Gale 这不涉及循环或数据库调用。那只是为了测试。这是独立的。这就像一个“简短的向导”。
    • 它是随机的,但不保证唯一...有可能,但不太可能,有重复。
    • @roryap 你甚至自己说过:“这并不是唯一的 5 个字符”
    • @Scottie -- 就像我说的,我对它进行了 1000 万次长度 6 的测试,它是独一无二的。我忘记了 5 的结果是什么。唯一性都是相对的。即使是 GUID 也不是真正唯一的。
    【解决方案3】:

    我将创建一个包含所有可能的 5 位字符串的表格(促销代码)。然后创建另一个包含两个字段的表 CampaignPromoCode:

    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()
    

    【讨论】:

    • 2 个单独的用户同时访问该表,仍然可以返回相同的代码。
    • 是的,但是您可以将方法编写为单例,这样一次只有 1 个用户可以使用它。
    • @Richard.Gale:可以防止并发访问。更大的问题是该表需要在超过 60,000,000 条记录时表现良好。
    • 为了防止并发问题,您可以使用单例模式,或者创建一个存储过程来锁定表,获取下一个代码,然后将其返回给您。这有一个很好的例子:stackoverflow.com/questions/3662766/…。 PromoCode 表不必那么大,基本上你可以减少它,因为我真的怀疑任何活动都会使用所有 2,176,782,336 个代码。
    【解决方案4】:

    这里的底线是,您可能应该回到管理层并告诉他们强制 100% 绝对唯一性的要求是一个成本非常高的要求,并且您可以以一小部分成本获得 99.9999% 的唯一性。然后使用 roryap 的代码生成一个随机的、大部分是唯一的代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多