Постоянное создание уникальных и случайных значений в определенном диапазоне в C#

Я реализую этот код в своем боте 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] и только один раз. Если все числа были сгенерированы один раз, все они могут быть сгенерированы снова.

Извините за мою плохую интерпретацию. Я усовершенствовал его с комментариями, которые все внесли.

Итак, если я правильно понял, вы хотите, чтобы программа работала до тех пор, пока не будут выбраны все возможные числа в диапазоне, который вы решили, при этом отбрасывая возможные двойные значения?

sMuLe 16.03.2022 12:10

да это я и имел в виду.

Tsain 16.03.2022 12:11
Turns out that the values sometimes are repeated. - такова природа случайности. вы вместо этого хотите взять числа 1-10 в случайный порядок?
Franz Gleichmann 16.03.2022 12:11

да, я полагаю, я не уверен, что полностью понимаю, что вы имели в виду.

Tsain 16.03.2022 12:13

Вместо помещения значений в массив по мере их использования рассмотрите возможность создания сначала полного массива, а затем перемешиваю это. Затем просто берите элементы из массива, пока он не станет пустым. Это логически одно и то же.

Damien_The_Unbeliever 16.03.2022 12:16

Я попробую, я думаю.

Tsain 16.03.2022 12:19

Я не понимаю. В какой момент можно снова «повторить» число? Вы не можете «бежать вечно» и иметь только 10 номеров. Правильно ли я понимаю: 10 раз вы выбираете число, вы хотите, чтобы оно было выбрано случайным образом из [1,10] и только один раз. Если все номера были выбраны один раз, все они могут быть выбраны снова. Это правильно?

Fildor 16.03.2022 12:21

Именно это я и имел в виду! Мне действительно нужно улучшить свой английский.

Tsain 16.03.2022 12:23
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
68
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вместо того, чтобы хранить в массиве, искать дубликаты и т. д., просто используйте HashSet Это принимает только уникальные значения, и если вы попытаетесь добавить дубликат, он будет просто проигнорирован.

Или вы можете просто проверить, существует ли он

HashSet<int> integerSet = new HashSet<int>();

if (hashSet.Contains(rng ))
  // element already exists in set
else 
   //Doesn't exist

Конечно, если вы просто продолжаете генерировать каждое число случайным образом, пока не сгенерируете все в диапазоне, то вы можете просто сгенерировать массив/список элементов с каждым числом и сэкономить время обработки!

Да, возможно, я могу использовать методы, отличные от массивов, для создания того, что я хочу, но мне нужно полностью понять, как работает «HashSet», так как я мог бы использовать его в других программах довольно скоро. Спасибо за ваше предложение!

Tsain 16.03.2022 12:29

Использование списка — хороший способ начать, но прежде чем продолжить комментарий, проверьте список документации

В светской беседе список представляет собой 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. Спасибо! Обязательно попробую и ваш.

Tsain 16.03.2022 15:22

И помните, после того, как вы нашли свой идеальный метод, чтобы пометить как принятый ответ, который сотворил магию, это хорошая привычка, которую нужно сохранить, чтобы помочь другим участникам!

sMuLe 16.03.2022 16:38

О да, конечно :) Я думаю, что каждое из этих решений работает. Думаю, я просто выберу тот, который я считаю наиболее полным и универсальным.

Tsain 16.03.2022 16:58

Да, вы правы, если каждый из них работает, вы обязательно должны отметить самый универсальный!

sMuLe 16.03.2022 17:27

Использование 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 и массивов! Это все, что меня волнует, и это определенно поможет мне в будущем, и, конечно же, я также воспользуюсь вашим предложением.

Tsain 16.03.2022 15:19
Ответ принят как подходящий

Вот решение, которое перемешивает значения и возвращает каждое значение до тех пор, пока список не будет исчерпан, а затем начинается все сначала:

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), мне не пришлось бы вставлять значения одно за другим в исходный массив, есть ли еще один способ сделать это?

Tsain 16.03.2022 15:03

Смотрите мою правку. Я оставил 10, но вы можете просто изменить nNumbers на любое разумное число. Для очень больших чисел вы, вероятно, инициализируете значения вне цикла и перетасовываете копию.

Palle Due 16.03.2022 15:23

На самом деле это уже перетасовка копии, поэтому просто переместите инициализацию из GenerateNewArray.

Palle Due 16.03.2022 16:01

Спасибо! Я могу понять большинство строк кода за короткое время.

Tsain 16.03.2022 16:59

Другие вопросы по теме