在 C# 中不断生成特定范围内的唯一和随机值
Constantly generating unique and random values in a specific range in C#
我在我的 discord 机器人中实现了这段代码,我希望每当我输入一个命令时都能生成从 1 到 10 的唯一数字(如上所述)。
事实证明,这些值有时会重复。因此,有人建议我添加一个数组 (used[ ]) 和一个循环,以便每次检查该值是否已经生成。
Random random = new Random();
int[] used = new int[10];
int rng = 0;
while (!used.Contains(rng))
{
rng = random.Next(1, 10);
}
/*
I wish to store the generated value "rng" to the "used" array.
e.g.
used[0] = rng
used[1] = rng
used[2] = rng
etc.
*/
Console.WriteLine("The number generated is " + Convert.ToString(rng));
但是,我不知道如何按排列顺序不断地向数组中添加值。 (如上评论所见)
更简单的方法,10次我想让系统生成一个数,这些数是从[1,10]中随机抽取的,而且只有一次。如果所有号码都生成过一次,可以免费重新生成。
对不起我的错误解释。我结合大家的评论完善了它。
与其存储在数组中、查找重复项等,不如使用 HashSet
这只接受唯一值,如果您尝试添加重复值,它将被简单地忽略。
或者您可以检查它是否存在
HashSet<int> integerSet = new HashSet<int>();
if (hashSet.Contains(rng ))
// element already exists in set
else
//Doesn't exist
当然,如果您一直随机生成每个数字,直到生成范围内的所有内容,那么您还不如简单地为每个数字生成一个 array/list 个元素,这样可以节省您自己的处理时间!
使用列表是一个很好的开始方式,但在继续评论之前请查看 list documentation
在闲聊中,列表是您选择的类型的 IEnumerable,在声明和实例化列表的情况下,您应该这样写 List<int> listName = new List<int>();
数组和列表之间的主要区别在于您可以在没有长度声明的情况下随时填充列表,为此您可以添加 listName.Add(2);
(2:您要添加的整数值)或删除值 listName.Remove(0)
(0:位置需要删除的列表,例如:0 是列表中的第一个值,因为位置 0 是开始)
Random random = new Random();
List<int> listName = new List<int>();
int rng = 0, counterOfValues = 0;
while (counterOfValues < 10)
{
rng = random.Next(1, 10);
If(!listName.Contains(rng))
{
listName.Add(rng);
Console.WriteLine("The number generated is " + listName.Last().ToString());
If(counterOfValues < 10)
{
counterOfValues++;
}
else
{
counterOfValues = 0;
}
}
}
这应该可以完成工作(“应该”是因为这是我脑子里写的一些代码,对于 caps ecc 很抱歉)
让我知道它是否按预期工作或者我们需要更正一些东西!
P.S:如果您希望用户 select 随机值的范围只需更改 counterOfValues
的最低值和 10's
的值较高的值
使用 HashSet 更好地了解您正在使用的数字。但就您而言,我认为最好使用 List 并删除元素:
var list = new List<int>();
for (int i = 1; i <= 10; i++)
list.Add(i);
// List is 0-index
// list = 1 2 3 4 5 6 7 8 9 10
int rng = random.Next(0, list.Count - 1);
// Suppose rgn=3: You get 3 value and remove it from the list
var value = list[rgn];
list.RemoveAt(rgn);
// list = 1 2 4 5 6 7 8 9 10
// Now you get a value between 0...8 (list.Count is 9)
rng = random.Next(0, list.Count - 1);
// Suppose rgn=3: You get 4 value and remove it from the list
var value = list[rgn];
list.RemoveAt(rgn);
// list = 1 2 5 6 7 8 9 10
// Now you get a value between 0...7 (list.Count is 8)
rng = random.Next(0, list.Count - 1);
// Suppose rgn=5: You get 7 value and remove it from the list
var value = list[rgn];
list.RemoveAt(rgn);
// list = 1 2 5 6 8 9 10
...
当列表只有一个元素时,您不需要使用随机。
当你的列表为空时,再次填充并重复整个过程。
在这种形式下,每次随机调用都会得到一个元素。使用 HashSet 你可能会尝试多次随机调用来获得一个未使用的数字。
A class 可能:
public class RandomClass
{
private readonly List<int> _list;
private readonly Random _random;
public RandomClass()
{
this._list = new List<int>();
this._random = new Random();
}
private void PopulateList()
{
for (int i = 1; i <= 10; i++)
this._list.Add(i);
}
public int GetNumber()
{
if (this._list.Count == 0)
{
this.PopulateList();
}
if (this._list.Count > 1)
{
int rng = this._random.Next(0, this._list.Count);
var value = this._list[rgn];
this._list.RemoveAt(rgn);
return value;
}
else
{
var value = this._list[0];
this._list.RemoveAt(0);
return value;
}
}
}
更新:关于列表、数组和其他集合的一些信息
数组和列表很相似。主要区别在于数组是连续的内存块,而列表(在内存中)不是块。数组的迭代速度更快(第一个元素的地址 + 乘以每个元素的大小)但是当你想要 add/remove 元素时更慢(你必须创建其他数组并复制元素)。从用户的角度来看,它们都用于存储项目列表并对其进行迭代。
其他非常有趣的集合是 HashSet 和 Dictionary。这些集合是集合,您不能使用索引访问它们的元素。您在此页面中看到了 HashSet 的示例:向集合中添加一个数字,如果您想知道该数字之前是否出现过,则查询它。该集合的关键是您可以使用散列访问它们的元素。您可以使用任何您想要的作为键而不是索引,并且算法可以快速获取元素在集合中的位置。字典就像一个哈希集,但您可以存储与键相关的附加数据。
在许多情况下,您会同时使用它们:一个用于按顺序迭代的列表和一个用于直接访问元素的字典。例如,您有很多按某种顺序显示的人物(名字、姓氏……)。您将它们存储在一个列表中,允许用户更改顺序(使用列表排序)。您使用列表是因为您多次显示和使用该顺序:您需要有序的人员。
但是你有很多很多人。假设您的程序允许按 Id 和 Name 搜索 Persons。每次用户搜索 Persons 时,您都必须迭代一个很长的列表。这效率不高。所以你定义了两个字典:
Dictionary<int, List<Person>> personsById;
Dictionary<string, List<Person>> personsByName;
此外,您有一个列表:
List<Person> allPersons;
我们还需要一些操作:
public void AddPerson(Person person)
{
Person existingPerson;
if (personsById.TryGetValue(person.Id, out existingPerson))
{
// Already exists: don't add duplicated persons
return;
}
personsById.Add(person.Id, person);
// Maybe more than one person with the same name. It's the reason why use a List here
List<Person> list;
if (personsByName.TryGetValue(person.Name, out list))
{
// There are other persons with this name, so the list exists. Simply add person to list
list.Add(person);
}
else
{
// Is the first person with this name: create the list and associate to the name
personsByName.Add(person.Name, new List<Person> { person });
}
// Always add to main list
allPersons.Add(person);
}
public void RemovePerson(Person person)
{
if (!personsById.TryGetValue(person.Id, out _))
{
// Not exists: do nothing
return;
}
personsById.Remove(person.Id);
// NOTE: Here we are removing person assuming that you never have different instances
// of person. If you can create different instances for a person, here we must iterate
// the lists to find the person
List<Person> list;
if (personsByName.TryGetValue(person.Name, out list))
{
list.Remove(person);
}
allPersons.Remove(person);
}
public Person GetPerson(int id)
{
return this.personsById.TryGetValue(id, out Person person) ? person : null;
}
public List<Person> GetPersons(string name)
{
return this.personsByName.TryGetValue(name, out List<Person> list) ? list : null;
}
这是一个非常基本的例子,但我认为这可能是一个很好的学习例子 classes.
这是一个解决方案,它会打乱值和 returns 每个值直到列表用完,然后重新开始:
int[] GenerateNewArray(int n)
{
// Define an array of values we wish to return and populate it
int[] baseValues = Enumerable.Range(1, n).ToArray();
Random rnd=new Random();
// Shuffle the array randomly using Linq
return baseValues.OrderBy(x => rnd.Next()).ToArray();
}
void Main()
{
int nNumbers = 10;
while (true)
{
// Generate a new randomized array
var values = GenerateNewArray(nNumbers);
// Print each value
for (int i=0;i<nNumbers;i++)
{
Console.WriteLine($"The number generated is {values[i]}");
}
}
}
编辑
参数化了唯一值的数量。
我在我的 discord 机器人中实现了这段代码,我希望每当我输入一个命令时都能生成从 1 到 10 的唯一数字(如上所述)。
事实证明,这些值有时会重复。因此,有人建议我添加一个数组 (used[ ]) 和一个循环,以便每次检查该值是否已经生成。
Random random = new Random();
int[] used = new int[10];
int rng = 0;
while (!used.Contains(rng))
{
rng = random.Next(1, 10);
}
/*
I wish to store the generated value "rng" to the "used" array.
e.g.
used[0] = rng
used[1] = rng
used[2] = rng
etc.
*/
Console.WriteLine("The number generated is " + Convert.ToString(rng));
但是,我不知道如何按排列顺序不断地向数组中添加值。 (如上评论所见)
更简单的方法,10次我想让系统生成一个数,这些数是从[1,10]中随机抽取的,而且只有一次。如果所有号码都生成过一次,可以免费重新生成。
对不起我的错误解释。我结合大家的评论完善了它。
与其存储在数组中、查找重复项等,不如使用 HashSet 这只接受唯一值,如果您尝试添加重复值,它将被简单地忽略。
或者您可以检查它是否存在
HashSet<int> integerSet = new HashSet<int>();
if (hashSet.Contains(rng ))
// element already exists in set
else
//Doesn't exist
当然,如果您一直随机生成每个数字,直到生成范围内的所有内容,那么您还不如简单地为每个数字生成一个 array/list 个元素,这样可以节省您自己的处理时间!
使用列表是一个很好的开始方式,但在继续评论之前请查看 list documentation
在闲聊中,列表是您选择的类型的 IEnumerable,在声明和实例化列表的情况下,您应该这样写 List<int> listName = new List<int>();
数组和列表之间的主要区别在于您可以在没有长度声明的情况下随时填充列表,为此您可以添加 listName.Add(2);
(2:您要添加的整数值)或删除值 listName.Remove(0)
(0:位置需要删除的列表,例如:0 是列表中的第一个值,因为位置 0 是开始)
Random random = new Random();
List<int> listName = new List<int>();
int rng = 0, counterOfValues = 0;
while (counterOfValues < 10)
{
rng = random.Next(1, 10);
If(!listName.Contains(rng))
{
listName.Add(rng);
Console.WriteLine("The number generated is " + listName.Last().ToString());
If(counterOfValues < 10)
{
counterOfValues++;
}
else
{
counterOfValues = 0;
}
}
}
这应该可以完成工作(“应该”是因为这是我脑子里写的一些代码,对于 caps ecc 很抱歉) 让我知道它是否按预期工作或者我们需要更正一些东西!
P.S:如果您希望用户 select 随机值的范围只需更改 counterOfValues
的最低值和 10's
的值较高的值
使用 HashSet 更好地了解您正在使用的数字。但就您而言,我认为最好使用 List 并删除元素:
var list = new List<int>();
for (int i = 1; i <= 10; i++)
list.Add(i);
// List is 0-index
// list = 1 2 3 4 5 6 7 8 9 10
int rng = random.Next(0, list.Count - 1);
// Suppose rgn=3: You get 3 value and remove it from the list
var value = list[rgn];
list.RemoveAt(rgn);
// list = 1 2 4 5 6 7 8 9 10
// Now you get a value between 0...8 (list.Count is 9)
rng = random.Next(0, list.Count - 1);
// Suppose rgn=3: You get 4 value and remove it from the list
var value = list[rgn];
list.RemoveAt(rgn);
// list = 1 2 5 6 7 8 9 10
// Now you get a value between 0...7 (list.Count is 8)
rng = random.Next(0, list.Count - 1);
// Suppose rgn=5: You get 7 value and remove it from the list
var value = list[rgn];
list.RemoveAt(rgn);
// list = 1 2 5 6 8 9 10
...
当列表只有一个元素时,您不需要使用随机。 当你的列表为空时,再次填充并重复整个过程。
在这种形式下,每次随机调用都会得到一个元素。使用 HashSet 你可能会尝试多次随机调用来获得一个未使用的数字。
A class 可能:
public class RandomClass
{
private readonly List<int> _list;
private readonly Random _random;
public RandomClass()
{
this._list = new List<int>();
this._random = new Random();
}
private void PopulateList()
{
for (int i = 1; i <= 10; i++)
this._list.Add(i);
}
public int GetNumber()
{
if (this._list.Count == 0)
{
this.PopulateList();
}
if (this._list.Count > 1)
{
int rng = this._random.Next(0, this._list.Count);
var value = this._list[rgn];
this._list.RemoveAt(rgn);
return value;
}
else
{
var value = this._list[0];
this._list.RemoveAt(0);
return value;
}
}
}
更新:关于列表、数组和其他集合的一些信息
数组和列表很相似。主要区别在于数组是连续的内存块,而列表(在内存中)不是块。数组的迭代速度更快(第一个元素的地址 + 乘以每个元素的大小)但是当你想要 add/remove 元素时更慢(你必须创建其他数组并复制元素)。从用户的角度来看,它们都用于存储项目列表并对其进行迭代。
其他非常有趣的集合是 HashSet 和 Dictionary。这些集合是集合,您不能使用索引访问它们的元素。您在此页面中看到了 HashSet 的示例:向集合中添加一个数字,如果您想知道该数字之前是否出现过,则查询它。该集合的关键是您可以使用散列访问它们的元素。您可以使用任何您想要的作为键而不是索引,并且算法可以快速获取元素在集合中的位置。字典就像一个哈希集,但您可以存储与键相关的附加数据。
在许多情况下,您会同时使用它们:一个用于按顺序迭代的列表和一个用于直接访问元素的字典。例如,您有很多按某种顺序显示的人物(名字、姓氏……)。您将它们存储在一个列表中,允许用户更改顺序(使用列表排序)。您使用列表是因为您多次显示和使用该顺序:您需要有序的人员。
但是你有很多很多人。假设您的程序允许按 Id 和 Name 搜索 Persons。每次用户搜索 Persons 时,您都必须迭代一个很长的列表。这效率不高。所以你定义了两个字典:
Dictionary<int, List<Person>> personsById;
Dictionary<string, List<Person>> personsByName;
此外,您有一个列表:
List<Person> allPersons;
我们还需要一些操作:
public void AddPerson(Person person)
{
Person existingPerson;
if (personsById.TryGetValue(person.Id, out existingPerson))
{
// Already exists: don't add duplicated persons
return;
}
personsById.Add(person.Id, person);
// Maybe more than one person with the same name. It's the reason why use a List here
List<Person> list;
if (personsByName.TryGetValue(person.Name, out list))
{
// There are other persons with this name, so the list exists. Simply add person to list
list.Add(person);
}
else
{
// Is the first person with this name: create the list and associate to the name
personsByName.Add(person.Name, new List<Person> { person });
}
// Always add to main list
allPersons.Add(person);
}
public void RemovePerson(Person person)
{
if (!personsById.TryGetValue(person.Id, out _))
{
// Not exists: do nothing
return;
}
personsById.Remove(person.Id);
// NOTE: Here we are removing person assuming that you never have different instances
// of person. If you can create different instances for a person, here we must iterate
// the lists to find the person
List<Person> list;
if (personsByName.TryGetValue(person.Name, out list))
{
list.Remove(person);
}
allPersons.Remove(person);
}
public Person GetPerson(int id)
{
return this.personsById.TryGetValue(id, out Person person) ? person : null;
}
public List<Person> GetPersons(string name)
{
return this.personsByName.TryGetValue(name, out List<Person> list) ? list : null;
}
这是一个非常基本的例子,但我认为这可能是一个很好的学习例子 classes.
这是一个解决方案,它会打乱值和 returns 每个值直到列表用完,然后重新开始:
int[] GenerateNewArray(int n)
{
// Define an array of values we wish to return and populate it
int[] baseValues = Enumerable.Range(1, n).ToArray();
Random rnd=new Random();
// Shuffle the array randomly using Linq
return baseValues.OrderBy(x => rnd.Next()).ToArray();
}
void Main()
{
int nNumbers = 10;
while (true)
{
// Generate a new randomized array
var values = GenerateNewArray(nNumbers);
// Print each value
for (int i=0;i<nNumbers;i++)
{
Console.WriteLine($"The number generated is {values[i]}");
}
}
}
编辑 参数化了唯一值的数量。