У меня есть список строк, содержащих множество хэштегов с текстами. бывший #csharp #java и т. д. каждый хэштег представляет собой собственную строку в списке. Теперь я хочу всегда случайным образом использовать console.writeline для 30 элементов этого списка.
Список
List<string> Hashtags = new List<string>();
Линия записи
foreach (var x in Hashtags) {
Console.WriteLine(x);
}
В идеале я никогда не хочу иметь один и тот же хэштег в случайных 30.
Все 30 строк начинаются с "#"?
Где твоя попытка решить проблему? Это не служба написания домашних заданий.
@BogdanDoicin да
@MischaMorf, значит, вы хотите случайным образом выбрать 30 элементов из своего списка, верно? Элементы списка различны?
@SteveTodd пробовал следующее: var shuffledcards = Hashtags.OrderBy(a => Guid.NewGuid()).ToList(); интервал я = 0; foreach (var x в хэштегах) { if (i>30) { Console.WriteLine(x); я++; } }
@JoãoPauloAmorim var shuffledcards = Hashtags.OrderBy(a => Guid.NewGuid()).ToList(); интервал я = 0; foreach (var x в хэштегах) { if (i>30) { Console.WriteLine(x); я++; } }
@MischaMorf - Guid.NewGuid() гарантируется только уникальность, а не случайность. Вы всегда должны использовать ГСЧ и никогда Guid из-за случайности.
@Enigmativity не знал, что
@MischaMorf не публикуйте попытки в комментариях. Обновите вопрос с помощью тех.





Позвонить 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 может возвращать одно и то же значение более одного раза.
@MischaMorf - это не работает, если вы пытаетесь избежать дубликатов.
@Enigmativity Я просто удаляю строку из списка строк после ее написания. Тогда получится :)
@MischaMorf - Однако изменять размер таких массивов довольно неэффективно.
Вы должны попробовать это:
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 - Это правда, но даже со списком из 1_000_000 элементов сортировка на моем компьютере занимает менее полсекунды. С положительной стороны, он гарантированно завершится и будет работать со списком источников, размер которого меньше запрашиваемой суммы.
@Enigmativity Тем не менее, это очень короткое, но ОЧЕНЬ неэффективное решение. Вы не только сортируете всю коллекцию, что, очевидно, является проблемой, но и даже больше - вам вообще не нужно ее сортировать, вам просто нужно перетасовать заданное количество элементов. Извините, но это не должен быть принятый ответ.
@zhulien - я согласен, что это неэффективно. Это O(n^2), но добавьте перетасовку Фишера-Йейтса, и она станет O(n). Это не так читабельно, но намного эффективнее, чем любой метод, упомянутый здесь до сих пор.
@Enigmativity Не уверен, что вы подразумеваете под «но это намного эффективнее, чем любой метод, упомянутый здесь до сих пор». Вы проверили мое решение, прежде чем комментировать?
@zhulien - Нет, ты прав. Я не проверял твой. Ты прав. Ваш более эффективен.
@zhulien - я обновил свой. Я чувствую, что сейчас на равных. :-)
Почему бы не использовать Enumerable.Range(0, 30).Select(x => Hashtags[rnd.Next(0, Hashtags.Count)]);;
@RandRandom - потому что это не гарантирует уникальные элементы.
Мог схитрить с ленивой оценкой 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 на моей машине .
У меня это работает, немного более многословно, но, надеюсь, легче следовать.
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, по крайней мере, даст вам это.
Он по-прежнему не компилируется, даже с вашими окончательными правками.
извините, я думал, что опубликовал окончательное редактирование, но сначала закрыл вкладку :)
@KrishanPatel этот код завершится ошибкой компиляции. В любом случае Count() и ToList() не нужны, потому что Hashtags уже является списком. Count() позвонит .Lenght внутри компании. ToList() создаст дорогую копию списка
Также нет метода DateTime.UtcNow.ToUnixTimeSeconds() - он должен быть DateTimeOffset.UtcNow.ToUnixTimeSeconds(). Теперь, даже если вы попытаетесь вызвать new Random(DateTimeOffset.UtcNow.ToUnixTimeSeconds()), это синтаксическая ошибка. И даже если бы это сработало, называть это таким образом в тысячу раз хуже, чем просто вызывать new Random() (который использует Environment.TickCount внутри).
Начало с max=Hashtags.Length означает, что всегда есть вероятность получить индекс вне допустимого диапазона. Так должно быть var max=Hashtags.Length -1;
Если вы сделаете Random статическим полем класса и инициализируете его только один раз, вы можете использовать его столько раз, сколько захотите, и будете получать случайные числа. Практически никогда не рекомендуется инициализировать Random внутри метода, который его использует. Кроме того, Random по умолчанию использует системные часы в качестве начального значения.
Для многократного выполнения следует использовать экземпляр такой жеRandom вместо того, чтобы пытаться создать новый или, что еще хуже, использовать секунды в качестве начального значения. Использование секунд означает, что в течение всей секунды будет создаваться последовательность чисел такой же.
Вы собираетесь var uniques = Hashtags стать ссылка копией Hashtags или создать клон?
Это должно сработать. Он эффективен, так как перемешивает только необходимое количество предметов, а не всю коллекцию. Вы передаете количество элементов, которые хотите извлечь из массива, в качестве параметра (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()) (для представления индексов).
Приятно, что перетасовывается только необходимое количество предметов.
(К вашему сведению, комментарий о .Select заключался в том, что он вам вообще не нужен; collection.ToList() вернет копию collection)
что ты уже испробовал? что не сработало?