繁体   English   中英

如何在C#中创建一个独特的随机字符序列?

[英]How can I create an unique random sequence of characters in C#?

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

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

编辑:

此功能将用于工作板网站,其中每个新的工作广告都将获得带有标题的自定义URL以及要在Twitter中使用的较短的URL。 也就是说,长期以来,独特的6个字符组合的总数将绰绰有余。

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

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

你真的需要'随机',还是'独特'就足够了?

Unique非常简单 - 只需将URL插入数据库,然后将该记录的顺序ID转换为base-n数字,该数字由您选择的characterset表示。

例如,如果您只想在序列中使用[AZ],则将记录的id转换为基数为26的数字,其中A = 1,B = 2,... Z = 26。 algothithm是递归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;
}

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

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

此外,您现在可以索引递增数字,该数字便宜,并且DB优化用于这些数字并且作为主键/外键进行散列,而不是可变长度随机字符串。

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

当你指出“随机生成的长期独特的6个字符组合的总数将足够多 ”时,您是否将生日悖论计算在计算中? 这通常是在一个范围内创建随机ID的任何尝试的祸根,该范围仅为所需的预期范围的1个数量级或更少。

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

我建议你采用编码顺序ID的想法。 为了避免用户能够简单地将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

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

根据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的方法,但该方法应该可行。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM