-2

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

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 个字符长或从10,000,000。

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

4

7 回答 7

6

您的方法在这里有很多问题已经被其他人解决了,所以我会回答一个您应该问但没有问的问题:

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

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

  • 该问题有一个“简单”的版本,无需递归即可解决
  • 每个不平凡的问题都可以简化为一个或多个严格更小的问题。
  • 反复将一个问题简化为一个较小的问题最终会导致尝试解决一个微不足道的问题,经过少量步骤,其中“小”我们的意思是,比如说,几百步,而不是几百万。(这个条件可以在“尾递归”语言中放宽;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;
}
于 2013-05-08T22:04:27.360 回答
5

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

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

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

    return num;
}

不同的方法,而我在这里

为什么不在这些值之间实现一些传递差异?即:第一个数字是一个,然后是两个等等。然后您需要做的就是获取最近的条目,然后添加一个。无需始终如一地进行数据库查询。

于 2013-05-08T21:29:24.793 回答
0

您没有指定您的数据库是什么。如果它只是一个已用数字的内存列表,您的代码可以像这样简单地完成:

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;
}
于 2013-05-08T21:35:17.047 回答
0

不完全的。

if (num is in table)
  return GenerateNumber();
else
  return num;

会起作用,但循环更容易/更安全:

int num;

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

return num;
于 2013-05-08T21:31:05.387 回答
0

不,不会的。您需要再次获取通过调用 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);
}
于 2013-05-08T21:31:53.417 回答
0

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

var rand = Guid.NewGuid().ToString()
于 2013-05-08T21:28:52.293 回答
0

使用迭代来避免因 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;
}
于 2013-05-08T21:33:16.497 回答