C# 递归检查数据库中的重复随机数

C# 递归检查数据库中的重复随机数,c#,C#,我创建一个随机数,然后检查它是否存在于数据库表中。如果是这样,我会生成另一个,然后再次检查,下面的工作也会正常吗 public int GenerateNumber() { Random r = new Random(); int num = r.Next(1000); //Psuedo-code if(num is in table) GenerateNumber(); return num; } 根据下面的答案,这里应该避免递归,

我创建一个随机数,然后检查它是否存在于数据库表中。如果是这样,我会生成另一个,然后再次检查,下面的工作也会正常吗

public int GenerateNumber()
{
    Random r = new Random();
    int num = r.Next(1000);

    //Psuedo-code
    if(num is in table)
        GenerateNumber();

    return num;
}
根据下面的答案,这里应该避免递归,我应该自动递增数字,因此,一个好的替代方法是从1开始自动递增,并用0填充,直到它是8个字符长,或者从10000000开始


另外,如果数据类型必须是varchar(8),该怎么办。如何自动递增一个数字,但将其存储在varchar(8)中?

这可能会导致非常糟糕的性能。使用,例如,Guid,用于此

var rand = Guid.NewGuid().ToString()

这不是一个需要通过递归来解决的问题。更不用说,如果数据库中有相当多的数字,并且循环了很多次,那么很快就会出现堆栈溢出错误。为什么不将其更改为迭代函数:

public int GenerateNumber()
{
    Random r = new Randon();
    int num = r.Next(1000);

    while(num is in database)
    {
        num = r.Next(1000);
    }

    return num;
}
当我在这里的时候,一种不同的方法

为什么不在这些值之间实现一些可传递的差异呢?例如:第一个数字是1,然后是2等等。然后你所需要做的就是获取最近的条目,并向其中添加一个。不需要持续不断地进行数据库查询。

不太需要

if (num is in table)
  return GenerateNumber();
else
  return num;
可以工作,但只循环更容易/更安全:

int num;

do
{
   num = r.Next(1000);
} while (num is in table);

return num;

不会的。您需要再次拨打GenerateNumber获得的号码

public int GenerateNumber()
{
    Random r = new Randon();
    int num = r.Next(1000);

    //Psuedo-code
    if(num is in table)
     num = GenerateNumber(); //num = added.

    return num;
}
现在,您不需要递归地解决这个问题,而在C中,这不是一个好主意,因为C不像其他语言那样进行尾部优化(它不会在编译时将递归调用更改为迭代调用)。这会起作用,但如果您在堆栈上做额外的工作,则可能会出现堆栈溢出错误。但是,既然您提出了问题,这就是如何修复代码以使其工作的

您可以通过执行以下操作轻松地将其更改为不使用递归:

while(num is in table){ //I always use brackets to be clear.
   num = r.Next(1000);
}

使用迭代避免因StackOverFlow异常而崩溃,如果您的表足够大,这将不可避免地发生

public int GenerateNumber()
{
    bool match = false;

    while (!match) {
        Random r = new Randon();
        int num = r.Next(1000);

    //Psuedo-code
    if(num is not in table)
        //insert
    }

    return num;
}

您没有指定数据库是什么。如果它只是一个内存中的已用数字列表,则您的代码可以简单地执行以下操作:

private HashSet<int> _usedNumbers = new HashSet<int>();
Random r = new Random(); //Search "Random is not random" on SO to see why I moved this out here.

public int GenerateNumber()
{
    int MaxNum = 1000;

    int num = r.Next(MaxNum);

    if(_usedNumbers.Count == MaxNum)
       throw new Exception("I ran out of numbers :(");

    while(_usedNumbers.Add(num) == false) //Add will return false if the number already was used.
    {
       num = r.Next(MaxNum );
    }

    return num;
}
private HashSet_usedNumbers=new HashSet();
随机r=新随机()//搜索“Random is not Random”,看看我为什么把这个移到这里。
公共整数生成器枚举器()
{
int MaxNum=1000;
int num=r.Next(MaxNum);
如果(_usedNumbers.Count==MaxNum)
抛出新异常(“我用完了数字:(”);
while(_usedNumbers.Add(num)==false)//Add将返回false(如果该数字已被使用)。
{
num=r.Next(MaxNum);
}
返回num;
}

您的方法存在许多问题,其他人已经解决了这些问题,因此我将回答一个您本应提出但没有提出的问题:

为了正确使用递归,问题必须具备哪些特征

除非您的解决方案具有以下所有特征,否则不得使用递归

  • 这个问题有一个“琐碎”的版本,不需要递归就可以解决
  • 每个非平凡问题都可以简化为一个或多个严格意义上的小问题
  • 反复地将一个问题简化为一个较小的问题,最终会导致在经过少量步骤后试图解决一个琐碎的问题,其中“小”的意思是,比如说,几百步,而不是几百万步。(这种条件在“尾部递归”语言中可以放宽;C#不是尾部递归语言。)
  • 小问题的解决方案总是可以有效地组合成大问题的解决方案
示例代码不显示任何这些特征;使用递归需要显示所有这些特征,因此在任何情况下都不应使用递归

让我给你举一个通过递归很好地解决的问题的例子:

树为空或由左、右子树组成;树从不包含循环。空树的高度为零;非空树的高度是从根到“最深”空子树的最长路径的长度。假设高度小于200,请编写一个确定树高度的方法

这个问题展示了可以用递归解决的问题的所有特征,因此我们可以这样做。每个递归程序都有以下模式:

  • 如果可以的话,解决这个小问题
  • 否则,将问题分解成更小的问题,递归地解决它们,然后组合解决方案
让我们这样做:

int Height(Tree tree)
{
    // Trivial case:
    if (tree.IsEmpty) return 0;
    // Non-trivial case: reduce the problem to two smaller problems:
    int leftHeight = Height(tree.Left);
    int rightHeight = Height(tree.Right);
    int height = Math.Max(leftHeight, rightHeight) + 1;
    return height;
}

@SamIam告诉我,我正在指向正确的路径。我不能使用GUID。它将数字传递给使用8个字符长的字符串的外部系统,否则我将使用GUID。@Xaisoft那么你应该设置一个好的算法,因为创建一个以前没有生成的随机数(通过进行一些查找)可以无限继续(?)理论上。你最好的选择是使用一个序列。不管你喜欢与否,所有其他答案都是不可接受的(至少我是如此),所以我们消除了堆栈溢出,现在只需要考虑非确定性的运行时间和竞争条件。进步!@ChrisCooney-谢谢,它实际上是100000000。有一个
do{}而
循环将减少代码中的冗余,我将尝试一下,谢谢。OP的递归模式在尾部递归语言中很好,在某些函数语言中甚至可能是惯用的。有人甚至会说这个问题满足了您提到的所有条件。1)普通版本是指没有检测到重复项2)问题不需要严格地更小,只要平均更小,如果最大数量大于表大小,则情况就是这样。3) 达到某个递归深度的几率呈指数下降。如果最大值足够大于