Получить случайный элемент из хэш-набора?

Я использую следующий фрагмент кода для загрузки моего текстового файла в хэш-набор.

HashSet<string> hashs = new HashSet<string>(File.ReadLines("textFile.txt"));

Интересно, есть ли простой способ получить из него случайную строку?

Предположим, что textFile.txt содержит 10 строк, я хотел бы рандомизировать и захватить одну из этих существующих строк.


person user1213488    schedule 18.05.2012    source источник
comment
Что вы пробовали? Пробовали ли вы использовать класс System.Random для генерации случайного числа от 0 до ‹количества строк›, а затем ссылаться на этот элемент по индексу? Все эти задачи уже задокументированы в библиотеке MSDN. mattgemmell.com/2008/12/08/what-have-you -пробовал   -  person David    schedule 18.05.2012


Ответы (6)


простой ответ, подобный принятому, возможен без перебора всего массива каждый раз:

private static readonly Random     random  = new Random();
private static readonly HashSet<T> hashset = new HashSet<T>();

...

T element = hashset.ElementAt(random.Next(hashset.Count));
person katbyte    schedule 11.04.2013
comment
ElementAt по-прежнему перечисляет элементы до тех пор, пока не достигнет указанного индекса, поэтому он не будет молниеносно быстрее. - person Zonko; 11.09.2015
comment
Если элементы хэш-наборов изменяются в многопоточной среде, это может вернуть InvalidOperationException (коллекция была изменена; операция перечисления может не выполняться). Этого можно избежать, используя блокировку (объект) в строке .ElementAt или .ToArray() для хэш-набора. и сохраните как новую переменную, используйте ее для ранда Count И для .ElementAt, но это может быть неэффективно с точки зрения памяти - person Ma Dude; 14.10.2018

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

person Attila    schedule 18.05.2012
comment
как будет выглядеть код для этого? не знаю как это написать :) - person user1213488; 18.05.2012
comment
1. Система Google. Случайный выбор. 2. Ознакомьтесь с уже предоставленной документацией и примерами кода, доступными по всему Интернету. 3. Учитесь, а не копируйте/вставляйте ответы. (Думаю, сегодня один из моих дурацких дней.) - person David; 18.05.2012

Или, может быть, более общее решение для любого перечислимого

public static class RandomExtensions
{
    private static readonly Random rnd = new Random();
    private static readonly object sync = new object();

    public static T RandomElement<T>(this IEnumerable<T> enumerable) {
        if (enumerable == null)
            throw new ArgumentNullException("enumerable");

        var count = enumerable.Count();

        var ndx = 0;
        lock (sync) 
            ndx = rnd.Next(count); // returns non-negative number less than max

        return enumerable.ElementAt(ndx); 
    }
}
person Vasea    schedule 18.05.2012
comment
ElementAt вызовет исключение для пустой коллекции. - person Sergey Berezovskiy; 18.05.2012
comment
@lazyberezovsky Если ElementAt выдает исключение, RandomElement также должен выдавать такое же исключение. В этом случае должен быть RandomElementOrDefault - person Vasea; 18.05.2012

Начиная с .Net Framework 3.5 вы можете использовать Linq с его Enumerable.First() метод расширения. Без указания какого-либо условия в качестве параметра этот метод вернет

первый элемент последовательности.

Вы должны учитывать, что использование Enumerable.First() требует, чтобы ваш HashSet<> содержал хотя бы один элемент. Чтобы проверить это предварительное условие, вы можете использовать HashSet<>.Count или через Linq с Enumerable.Any() снова без указания условия.

HashSet<T> hashSet = new HashSet<T>();

...

if(hashSet.Any())
{
  T randomElement = hashSet.First()
}

В качестве альтернативы описанному выше подходу вы можете использовать Enumerable.FirstOrDefault()< /a>, если вы хотите получить любое значение по умолчанию, если HashSet<T> пусто.

T randomElement = hashSet.FirstOrDefault(default(T));
person bbenno    schedule 19.11.2019
comment
Как это вернет случайный элемент? - person derHugo; 03.04.2021
comment
@derHugo HashSet — это несортированная структура, поэтому первая будет случайной. - person orion_tvv; 31.05.2021
comment
@orion_tvv Я думаю, он по-прежнему будет возвращать один и тот же элемент каждый раз, когда вы вызываете это ... random != unsorted - person derHugo; 31.05.2021
comment
Да, если вы хотите получить другой элемент несколькими вызовами, вам не следует использовать HashSet, и вам придется заплатить за преобразование в структуру, подобную массиву. Но для большинства случаев первый пункт является хорошим компромиссом. - person orion_tvv; 31.05.2021

Если вы планируете рисовать несколько случайных значений, эффективным способом будет использование словаря с целочисленными ключами для хранения информации.

HashSet<string> hashs = new HashSet<string>();
Dictionary<int, string> lookup = new Dictionary<int, string>();
foreach (string line in File.ReadLines("textFile.txt")) {
    if (hashs.Add(line)) {
        lookup.Add(lookup.Count, line);
    }
}
        
int randomInt = new Random().Next(lookup.Count);
string randomLine = lookup[randomInt];

(В этом примере вместо этого вы можете использовать список, но со словарем вы также можете удалить отдельные элементы, не влияя на порядок).

person Emil Forslund    schedule 03.06.2021

person    schedule
comment
Довольно неэффективная производительность. Не то чтобы я знаю лучший способ, но просто говорю. - person batman; 17.06.2017