Возьмите 30 случайных элементов из списка

У меня есть список строк, содержащих множество хэштегов с текстами. бывший #csharp #java и т. д. каждый хэштег представляет собой собственную строку в списке. Теперь я хочу всегда случайным образом использовать console.writeline для 30 элементов этого списка.

Список


List<string> Hashtags = new List<string>();

Линия записи


 foreach (var x in Hashtags) {

                Console.WriteLine(x);
            }

В идеале я никогда не хочу иметь один и тот же хэштег в случайных 30.

что ты уже испробовал? что не сработало?

João Paulo Amorim 05.06.2019 14:16

Все 30 строк начинаются с "#"?

Bogdan Doicin 05.06.2019 14:16

Где твоя попытка решить проблему? Это не служба написания домашних заданий.

Steve Todd 05.06.2019 14:17

@BogdanDoicin да

Mischa Morf 05.06.2019 14:17

@MischaMorf, значит, вы хотите случайным образом выбрать 30 элементов из своего списка, верно? Элементы списка различны?

Bogdan Doicin 05.06.2019 14:18

@SteveTodd пробовал следующее: var shuffledcards = Hashtags.OrderBy(a => Guid.NewGuid()).ToList(); интервал я = 0; foreach (var x в хэштегах) { if (i>30) { Console.WriteLine(x); я++; } }

Mischa Morf 05.06.2019 14:19

@JoãoPauloAmorim var shuffledcards = Hashtags.OrderBy(a => Guid.NewGuid()).ToList(); интервал я = 0; foreach (var x в хэштегах) { if (i>30) { Console.WriteLine(x); я++; } }

Mischa Morf 05.06.2019 14:19

@MischaMorf - Guid.NewGuid() гарантируется только уникальность, а не случайность. Вы всегда должны использовать ГСЧ и никогда Guid из-за случайности.

Enigmativity 05.06.2019 14:20

@Enigmativity не знал, что

Mischa Morf 05.06.2019 14:23

@MischaMorf не публикуйте попытки в комментариях. Обновите вопрос с помощью тех.

Franck 05.06.2019 14:26
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
10
130
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Позвонить 30 раз Random.next

https://docs.microsoft.com/en-us/dotnet/api/system.random.next

var random = new Random();

//calls this n times in a loop:
Console.writeline(Hashtags[random.next(Hashtags.Count])

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

dvo 05.06.2019 14:27

@MischaMorf - это не работает, если вы пытаетесь избежать дубликатов.

Enigmativity 05.06.2019 14:31

@Enigmativity Я просто удаляю строку из списка строк после ее написания. Тогда получится :)

Mischa Morf 05.06.2019 14:32

@MischaMorf - Однако изменять размер таких массивов довольно неэффективно.

Enigmativity 05.06.2019 14:39
Ответ принят как подходящий

Вы должны попробовать это:

var rnd = new Random();

foreach (var x in Hashtags.OrderBy(x => rnd.Next()).Take(30))
{
    Console.WriteLine(x);
}

Это имеет O(n^2) сложность, но легко читается.

Если вам нужна эффективность, попробуйте перетасовку Фишера-Йейтса, это O(n), но менее читабельно:

var take = 30;

var rnd = new Random();

for (var i = 0; i < (Hashtags.Count < take ? Hashtags.Count : take); i++)
{
    var j = rnd.Next(Hashtags.Count);
    (Hashtags[i], Hashtags[j]) = (Hashtags[j], Hashtags[i]);
}

foreach (var x in Hashtags.Take(take))
{
    Console.WriteLine(x);
}

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

[ThreadStatic]
private static Random rnd = new Random();

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

Magnus 05.06.2019 14:27

@Magnus - Это правда, но даже со списком из 1_000_000 элементов сортировка на моем компьютере занимает менее полсекунды. С положительной стороны, он гарантированно завершится и будет работать со списком источников, размер которого меньше запрашиваемой суммы.

Enigmativity 05.06.2019 14:35

@Enigmativity Тем не менее, это очень короткое, но ОЧЕНЬ неэффективное решение. Вы не только сортируете всю коллекцию, что, очевидно, является проблемой, но и даже больше - вам вообще не нужно ее сортировать, вам просто нужно перетасовать заданное количество элементов. Извините, но это не должен быть принятый ответ.

zhulien 05.06.2019 14:58

@zhulien - я согласен, что это неэффективно. Это O(n^2), но добавьте перетасовку Фишера-Йейтса, и она станет O(n). Это не так читабельно, но намного эффективнее, чем любой метод, упомянутый здесь до сих пор.

Enigmativity 05.06.2019 15:03

@Enigmativity Не уверен, что вы подразумеваете под «но это намного эффективнее, чем любой метод, упомянутый здесь до сих пор». Вы проверили мое решение, прежде чем комментировать?

zhulien 05.06.2019 15:08

@zhulien - Нет, ты прав. Я не проверял твой. Ты прав. Ваш более эффективен.

Enigmativity 05.06.2019 15:12

@zhulien - я обновил свой. Я чувствую, что сейчас на равных. :-)

Enigmativity 05.06.2019 15:15

Почему бы не использовать Enumerable.Range(0, 30).Select(x => Hashtags[rnd.Next(0, Hashtags.Count)]);;

Rand Random 05.06.2019 15:20

@RandRandom - потому что это не гарантирует уникальные элементы.

Enigmativity 05.06.2019 15:49

Мог схитрить с ленивой оценкой LINQ Enumerable.Range(0, int.MaxValue).Select(x => rnd.Next(0, list.Count - 1)).Distinct().Take(30).Select(x => list[x]);, хотя красота пропала :) - С небольшим тестом этот подход кажется вдвое быстрее, чем ваше решение - хотя это с 2,5 миллиардами записей в списке, о котором мы говорим 4ms против 9ms на моей машине .

Rand Random 05.06.2019 16:04

У меня это работает, немного более многословно, но, надеюсь, легче следовать.

 var random = new Random();
 var uniques = Hashtags; 

 for (var i = 0; i < 30; i++) {  
     var index =  random.Next(0, uniques.Count());
     Console.WriteLine(uniques[index]);
     uniques.RemoveAt(index); 
 }

извинения - вы разместили это до моих последних правок. очевидно, вам нужно добавить некоторые операторы использования вверху, но я думал, что IDE, по крайней мере, даст вам это.

aspirant_sensei 05.06.2019 14:51

Он по-прежнему не компилируется, даже с вашими окончательными правками.

Enigmativity 05.06.2019 14:53

извините, я думал, что опубликовал окончательное редактирование, но сначала закрыл вкладку :)

aspirant_sensei 05.06.2019 14:55

@KrishanPatel этот код завершится ошибкой компиляции. В любом случае Count() и ToList() не нужны, потому что Hashtags уже является списком. Count() позвонит .Lenght внутри компании. ToList() создаст дорогую копию списка

Panagiotis Kanavos 05.06.2019 14:55

Также нет метода DateTime.UtcNow.ToUnixTimeSeconds() - он должен быть DateTimeOffset.UtcNow.ToUnixTimeSeconds(). Теперь, даже если вы попытаетесь вызвать new Random(DateTimeOffset.UtcNow.ToUnixTimeSeconds()), это синтаксическая ошибка. И даже если бы это сработало, называть это таким образом в тысячу раз хуже, чем просто вызывать new Random() (который использует Environment.TickCount внутри).

Enigmativity 05.06.2019 14:56

Начало с max=Hashtags.Length означает, что всегда есть вероятность получить индекс вне допустимого диапазона. Так должно быть var max=Hashtags.Length -1;

Panagiotis Kanavos 05.06.2019 14:56

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

Rufus L 05.06.2019 15:00

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

Panagiotis Kanavos 05.06.2019 15:01

Вы собираетесь var uniques = Hashtags стать ссылка копией Hashtags или создать клон?

Enigmativity 05.06.2019 15:11

Это должно сработать. Он эффективен, так как перемешивает только необходимое количество предметов, а не всю коллекцию. Вы передаете количество элементов, которые хотите извлечь из массива, в качестве параметра (elementCount).

private static Random randomGenerator = new Random();

static void Main()
{
    var hashtags = new List<string>() { "c#", "javascript", "ef", "asp.net" };

    var result = GetRandomItems<string>(hashtags, 2);

    foreach (var item in result)
    {
        Console.WriteLine(item);
    }
}

private static IEnumerable<T> GetRandomItems<T>(IEnumerable<T> collection, int elementCount)
{
    var collectionCount = collection.Count();

    if (elementCount > collectionCount)
    {
        elementCount = collectionCount;
    }

    var collectionCopy = collection.ToList();

    var randomIndex = randomGenerator.Next(0, collectionCopy.Count);

    for (var index = 0; index < elementCount; index++)
    {
        var tempElement = collectionCopy[index];

        collectionCopy[index] = collectionCopy[randomIndex];
        collectionCopy[randomIndex] = tempElement;

        randomIndex = randomGenerator.Next(index + 1, collectionCopy.Count);
    }

    return collectionCopy.Take(elementCount);
}

Хороший. Вместо создания копии списка, возможно, было бы более эффективно перетасовать List<int>(collection.Count()) (для представления индексов).

Rufus L 05.06.2019 15:08

Приятно, что перетасовывается только необходимое количество предметов.

Enigmativity 05.06.2019 15:09

(К вашему сведению, комментарий о .Select заключался в том, что он вам вообще не нужен; collection.ToList() вернет копию collection)

Rufus L 05.06.2019 15:11

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