当使用按钮但数字不能重复时,如何从一个范围生成随机数字



我正在创建一个测试,并使用1-20个数字(主键(中的随机数进行im

Random r = new Random();
int rInt = r.Next(1, 9);

数字(主键(,然后用于查询选择5个随机数,但问题是我得到了重复的问题,因为数字重复

string SQL = "SELECT QuestionText,CorrectAnswer,WrongAnswer1,WrongAnswer2,WrongAnswer3 FROM Question Where QuestionID = " + rInt;

我试过一些方法来修复它,但它不起作用,而且已经没有想法了,有人有什么建议吗?

只需向数据库索取即可:

string SQL = @"
SELECT TOP 5 QuestionText,CorrectAnswer,WrongAnswer1,WrongAnswer2,WrongAnswer3 
FROM Question 
ORDER BY NewID()";

如果/当你超出这个范围时,还有一个更优化的解决方案:

string SQL = @"
WITH cte AS 
(
SELECT TOP 5 QuestionId FROM Questions ORDER BY NEWID()
)
SELECT QuestionText,CorrectAnswer,WrongAnswer1,WrongAnswer2,WrongAnswer3
FROM cte c
JOIN Questions q 
ON q.QuestionId = c.QuestionId 
";

第二个查询将执行得更好(假设QuestionId是主键(,因为它只需要读取主索引(可能已经在内存中(,生成Guid,使用最有效的方法选择前5个,然后使用主键查找这5条记录。

对于数量较少的问题,第一个查询应该可以正常工作,但我认为它可能会导致表扫描,并给tempdb带来一些压力,因此,如果您的问题是varchar(max(并且非常长,或者您在一些版本的Sql Server中使用非常小的tempdb有数以万计的问题,它可能不会很好地执行。

这样的东西可能会对你有用:

[ThreadStatic]
private static Random __random = null;
public int[] Get5RandomQuestions()
{
__random = __random ?? new Random(Guid.NewGuid().GetHashCode()); // approx one in 850 chance of seed collision
using (var context = new MyDBContext())
{
var questions = context.Questions.Select(x => x.Question_ID).ToArray();
return questions.OrderBy(_ => __random.Next()).Take(5).ToArray();
}
}

另一种服务器端方法:

private static Random _r = new Random();
...
var seed = _r.NextDouble();
using var context = new SomeContext();
var questions = context.Questions
.OrderBy(p => SqlFunctions.Checksum(p.Id * seed))
.Take(5);

注意:校验和不是防弹的,有限制。这种方法不应用于生死攸关的情况下生成问答题


根据请求:

SqlFunctions.Checksum本质上将生成一个散列并按其排序

CHECKSUM([Id] * <seed>) AS [C1], 
...
ORDER BY [C1] ASC

CHECKSUM(Transact-SQL(

CHECKSUM函数返回在表上计算的校验和值行或表达式列表上。使用CHECKSUM生成哈希索引。

CHECKSUM通过其参数计算一个散列值,称为校验和列表使用此哈希值可以生成哈希索引。哈希索引将如果CHECKSUM函数具有列参数,并且索引为建立在计算的CHECKSUM值之上。这可以用于相等在列上搜索。

注意,如前所述,校验和不是防弹的,它返回一个int(照原样(,然而,当以这种方式使用具有唯一Id的校验和时,对于较小的数据集,冲突或重复的机会非常小,它的性能也相当好

因此,在一个有1000万条记录的生产数据库中多次运行,没有发生冲突。

就速度而言,它可以在75ms内进入前5名,但由EF 生成时速度较慢

为NewId投标的cte解决方案约为125毫秒。

Linq.Distinct()方法太好了,不能在这里不使用

据我所知,最简单的方法是如下,使用一种方法创建一个无限的随机数流,然后可以很好地与Linq:进行争论

using System.Linq;
IEnumerable<int> GenRandomNumbers()
{
var random = new Random();
while (true)
{
yield return rand.Next(1, 20);
}
}
var numbers = GenRandomNumbers()
.Distinct()
.Take(5)
.ToArray();

尽管由于生成器方法的闭环,它看起来将永远运行,但由于它的产生方式,它只会运行到生成5个不同的数字。

尝试从数据库中选择所有问题。假设你在"问题"集合中有它们,然后你可以尝试Questions.OrderBy(y => Guid.NewGuid()).ToList()

最新更新