Я реализую этот код в своем боте Discord, где я хочу иметь возможность генерировать уникальные числа от 1 до 10 (как было предложено выше) всякий раз, когда я ввожу одну команду.
Оказывается, значения иногда повторяются. Поэтому мне предложили добавить массив (использовал[ ]) и цикл, чтобы каждый раз проверять, не сгенерировано ли уже значение.
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] и только один раз. Если все числа были сгенерированы один раз, все они могут быть сгенерированы снова.
Извините за мою плохую интерпретацию. Я усовершенствовал его с комментариями, которые все внесли.
да это я и имел в виду.
Turns out that the values sometimes are repeated.
- такова природа случайности. вы вместо этого хотите взять числа 1-10 в случайный порядок?
да, я полагаю, я не уверен, что полностью понимаю, что вы имели в виду.
Вместо помещения значений в массив по мере их использования рассмотрите возможность создания сначала полного массива, а затем перемешиваю это. Затем просто берите элементы из массива, пока он не станет пустым. Это логически одно и то же.
Я попробую, я думаю.
Я не понимаю. В какой момент можно снова «повторить» число? Вы не можете «бежать вечно» и иметь только 10 номеров. Правильно ли я понимаю: 10 раз вы выбираете число, вы хотите, чтобы оно было выбрано случайным образом из [1,10] и только один раз. Если все номера были выбраны один раз, все они могут быть выбраны снова. Это правильно?
Именно это я и имел в виду! Мне действительно нужно улучшить свой английский.
Вместо того, чтобы хранить в массиве, искать дубликаты и т. д., просто используйте HashSet Это принимает только уникальные значения, и если вы попытаетесь добавить дубликат, он будет просто проигнорирован.
Или вы можете просто проверить, существует ли он
HashSet<int> integerSet = new HashSet<int>();
if (hashSet.Contains(rng ))
// element already exists in set
else
//Doesn't exist
Конечно, если вы просто продолжаете генерировать каждое число случайным образом, пока не сгенерируете все в диапазоне, то вы можете просто сгенерировать массив/список элементов с каждым числом и сэкономить время обработки!
Да, возможно, я могу использовать методы, отличные от массивов, для создания того, что я хочу, но мне нужно полностью понять, как работает «HashSet», так как я мог бы использовать его в других программах довольно скоро. Спасибо за ваше предложение!
Использование списка — хороший способ начать, но прежде чем продолжить комментарий, проверьте список документации
В светской беседе список представляет собой 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;
}
}
}
Вероятно, это должно работать («должно», потому что это какой-то код, который я написал из головы, так что извините за заглавные буквы и т. д.) Дайте мне знать, если это работает так, как задумано, или нам нужно что-то исправить!
P.S. Если вы хотите, чтобы пользователь выбирал диапазон случайных значений, просто измените значение counterOfValues
на наименьшее значение, а 10's
на большее значение.
Я только недавно занялся кодированием, поэтому, с моей точки зрения, есть еще части, которые я не могу понять. Тем не менее, вы дали очень подробное объяснение того, как использовать list
. Спасибо! Обязательно попробую и ваш.
И помните, после того, как вы нашли свой идеальный метод, чтобы пометить как принятый ответ, который сотворил магию, это хорошая привычка, которую нужно сохранить, чтобы помочь другим участникам!
О да, конечно :) Я думаю, что каждое из этих решений работает. Думаю, я просто выберу тот, который я считаю наиболее полным и универсальным.
Да, вы правы, если каждый из них работает, вы обязательно должны отметить самый универсальный!
Использование HashSet лучше знать числа, которые вы используете. Но в вашем случае я думаю, что лучше использовать список и удалить элементы:
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
...
Когда в списке есть один элемент, вам не нужно использовать random. Когда ваш список пуст, заполните его снова и повторите весь процесс.
В этой форме вы всегда получаете элемент при каждом случайном вызове. Используя HashSet, вы можете попробовать много случайных вызовов, чтобы получить неиспользуемый номер.
Класс для этого может быть:
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;
}
}
}
ОБНОВЛЕНИЕ: кое-что о списках, массивах и других коллекциях
Массив и список очень похожи. Основное отличие состоит в том, что массив — это непрерывный блок памяти, а список (в памяти) — не блок. Массив быстрее повторяется (адрес первого элемента + умножение на размер каждого элемента), но медленнее, когда вы хотите добавить/удалить элементы (вы должны создать другой массив и скопировать элементы). С точки зрения пользователя оба они используются для хранения списка элементов и их повторения.
Другими очень интересными коллекциями являются HashSet и Dictionary. Эти коллекции являются наборами, вы не можете получить доступ к их элементам с помощью индекса. Вы видели пример для HashSet на этой странице: добавьте число в набор и запросите его, если хотите узнать, появлялось ли это число раньше. Ключевым моментом этих наборов является то, что вы получаете доступ к их элементам с помощью хеша. Вместо индекса вы используете все, что хотите, в качестве ключа, и алгоритм быстро получает позицию элемента в наборе. Dictionary похож на HashSet, но вы можете хранить дополнительные данные, связанные с ключом.
Во многих случаях вы будете использовать их оба: список для последовательной итерации и словарь для прямого доступа к элементам. Например, у вас есть много лиц, которых вы показываете в некотором порядке (имя, фамилия...). Вы сохраняете их в списке и позволяете пользователю изменять порядок (используя сортировку списка). Вы используете список, потому что вы показываете и используете этот порядок много раз: вам нужны упорядоченные лица.
Но у вас много, много Личностей. Предположим, что ваша программа позволяет искать людей по идентификатору и имени. Каждый раз, когда пользователь ищет людей, вы должны перебирать очень длинный список. Это не эффективно. Итак, вы определяете два словаря:
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;
}
Это очень простой пример, но я думаю, что он может быть хорошим, чтобы узнать об этих классах.
Я новичок в кодировании, поэтому я не могу понять все, что вы упомянули. Однако вы помогли мне различать использование HashSet, List и массивов! Это все, что меня волнует, и это определенно поможет мне в будущем, и, конечно же, я также воспользуюсь вашим предложением.
Вот решение, которое перемешивает значения и возвращает каждое значение до тех пор, пока список не будет исчерпан, а затем начинается все сначала:
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]}");
}
}
}
РЕДАКТИРОВАТЬ Параметризовано количество уникальных значений.
Я никогда не думал об этом так, но если бы я рандомизировал значение между 1 и большим числом (например, 9999), мне не пришлось бы вставлять значения одно за другим в исходный массив, есть ли еще один способ сделать это?
Смотрите мою правку. Я оставил 10, но вы можете просто изменить nNumbers на любое разумное число. Для очень больших чисел вы, вероятно, инициализируете значения вне цикла и перетасовываете копию.
На самом деле это уже перетасовка копии, поэтому просто переместите инициализацию из GenerateNewArray.
Спасибо! Я могу понять большинство строк кода за короткое время.
Итак, если я правильно понял, вы хотите, чтобы программа работала до тех пор, пока не будут выбраны все возможные числа в диапазоне, который вы решили, при этом отбрасывая возможные двойные значения?