【问题标题】:How can I create an unique random sequence of characters in C#?如何在 C# 中创建唯一的随机字符序列?
【发布时间】:2010-11-19 12:23:37
【问题描述】:

我正在我的应用程序中实现 URL 缩短功能,以便为我的用户提供可在 Twitter 中使用的更短的替代 URL。关键是要独立于提供相同服务的缩短服务,并将其作为我的网络应用程序的一项功能。

创建大约 6 个字符的唯一随机字符序列的最佳方法是什么?我计划将其用作我的数据库中具有备用 URL 的项目的索引。

编辑:

此功能将用于招聘委员会网站,每个新招聘广告都将获得一个自定义 URL,其中包含标题以及在 Twitter 中使用的较短的标题。也就是说,在很长一段时间内,唯一的 6 个字符组合的总数将绰绰有余。

【问题讨论】:

    标签: c# string random


    【解决方案1】:

    我用它来做一些非常相似的事情。我并不担心它的速度,因为它将是一个很少使用的事件和表。但是可以根据需要增加字符串。

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

    【讨论】:

      【解决方案2】:

      当您声明“唯一 6 个字符组合的总数将在很长一段时间内绰绰有余”时,您是否将 birthday paradox 考虑到您的计算中?这通常是在一个数量级或小于所需预期范围的范围内创建随机 ID 的任何尝试的祸根。

      要创建真正的随机 ID,您需要创建一个生成新随机值的循环,检查该值是否已被使用,然后在需要时重复该循环。生日悖论意味着您很快就会到达生成的许多值已经在使用中(尽管只消耗了总范围的一小部分),这导致程序随着时间的推移变得越来越慢,直到花费数千生成每个 ID 的尝试(和数据库查找)次数。

      我建议您采用对顺序 ID 进行编码的想法。为了避免用户能够简单地增加/减少 URL 中的值以“探索”的问题,您可以使用组合位移和字母的替代有序列表(而不是 1=a,2=b 使用 1= t、2=j 等)。

      【讨论】:

        【解决方案3】:

        您真的需要“随机”,还是“唯一”就足够了?

        唯一非常简单 - 只需将 URL 插入数据库,然后将该记录的顺序 id 转换为由您选择的字符集表示的以 n 为基数的数字。

        例如,如果您只想在序列中使用 [A-Z],则将记录的 id 转换为以 26 为基数的数字,其中 A=1, B=2,... Z=26。算法是递归的 div26/mod26,其中商为所需字符,余数用于计算下一个字符。

        然后在检索 URL 时,执行反函数,即将 base-26 数字转换回十进制。执行 SELECT URL WHERE 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;
        }
        

        【讨论】:

        • 感谢您的回答!我有一个问题:encodingBase 是什么?
        • @Maxim - 一年多以前很难记住,但我想这是编码词的长度;即对于像上面这样的 base-26 编码,它将是 26。但是我认为代码中还有一个错误!它真的应该是 > 1
        【解决方案4】:

        按照 Reed Copsey 的回答思路,我提供以下代码:

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

        您可能想想出一种存储当前 ID 的方法,但这应该可行。

        【讨论】:

          【解决方案5】:

          不要保留所有可能值的表格,只需保留您已使用的值的表格。使用 random 函数生成 6 个随机值,从 1 到 26,从中生成字符串并将其保存在数组或表中。如果它已经存在,您可以 (a) 生成另一个字符串,或 (b) 在表中移动到下一个可用(缺失)的 6 字母字符串并使用该值。 (b) 随着表格的填满,效率会更高。

          【讨论】:

            【解决方案6】:

            【讨论】:

              【解决方案7】:

              随机生成器的用处仅限于防止用户插入随机 URL 以查找他们不应该链接的内容。如果这不是您的目标,那么顺序 ID 应该可以正常工作。如果您只是不想给用户留下他们正在使用“婴儿”技术的印象(当他们看到他们的招聘广告是 #000001 时),为什么不从某个任意值开始序列?

              【讨论】:

                【解决方案8】:

                我会使用自动编号系统,并创建一个算法来生成密钥。即 1 = a、2 = b、27 = aa 等

                您可以使用数据库自​​动编号来保证您的 URL 是唯一的,并且您可以在 DB 或业务层中的存储过程中计算 URL?

                此外,您现在可以索引递增的数字,这很便宜,并且 DB 已针对这些数字进行了优化,可用作主键/外键,而不是可变长度的随机字符串。

                【讨论】:

                  【解决方案9】:

                  在这里更多地考虑这个是一个想法。

                  您可以从键表开始,递增字符 AAAAAA - ZZZZZZ。

                  然后在每次插入新 URL 时从该表中随机选择,并从可用键中删除。

                  想法?

                  对于随机选择试试这个link

                  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
                  

                  【讨论】:

                    【解决方案10】:

                    制作唯一序列的最简单方法是按顺序执行此操作,即:aaaaaa aaaaab aaaaac ...这些不一定是最漂亮的,但可以保证前 12230590463 个序列的唯一性(前提是您使用 az 和 AZ 作为唯一人物)。如果您需要更多的 URL,则需要添加第七个字符。

                    不过,它们不是随机序列。如果您制作随机字符,只需选择 48、6 次的随机字符。不过,您需要检查现有数据库中的“已使用”序列,因为您更有可能发生冲突。

                    【讨论】:

                    • 它确实会导致更多的工作,并且您最终使用的值越多,可能会超时。
                    • 是的。 Sequential 避免了所有这些,但您最终会得到顺序值(可能会也可能不会)。对于 twitter URL,我会使用 seq.值,因为它们真正的“随机性”实际上并不重要,而且您将拥有大量独特的值,以及非常简单的生成
                    • 如果您将 0-9 作为字符集的一部分包括在内,那么您有 56800235584 个唯一值(~4.6x 只是 a-z、A-Z)。我知道他刚刚提到了“字符”,但这可能包括数字。如果您还包括一些其他字符,例如 *$%() 等,那也会增加一点(至少是原始集合的约 9 倍)。
                    猜你喜欢
                    • 2014-10-20
                    • 1970-01-01
                    • 2021-02-21
                    • 1970-01-01
                    • 2014-04-11
                    • 1970-01-01
                    • 2013-07-21
                    • 2020-12-05
                    • 1970-01-01
                    相关资源
                    最近更新 更多