выберите случайный элемент из списка в foreach [duplicate]

Как уже упоминалось, вы не можете переопределить закрытый метод S4 «+». Однако вам не нужно определять новый класс, чтобы определить функцию сложения для строк; это не идеально, поскольку оно заставляет вас преобразовать класс строк и, таким образом, привести к более уродливому коду. Вместо этого можно просто переписать функцию «+»:

  «+» = function (x, y) {if (is.character (x) || is.character (y))  {return (paste (x, y, sep = ""))} else {.Primitive ("+") (x, y)}}  

Тогда все должны работать как Ожидаемое:

  1 + 4 1:10 + 4 «Помощь» + «Я»  

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

4
задан Melina 7 November 2013 в 14:50
поделиться

5 ответов

300 не очень много, поэтому да, сделайте это List:

IQueryable<Detail> details = ...
IList<Detail> detailList = details.ToList();

И теперь вы можете выбрать случайный элемент:

var randomItem = detailList[rand.Next(detailList.Count)];

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

Итак, используйте стандартный алгоритм shuffle , а затем выберите первые 50:

Shuffle(detailList);
var selection = detailList.Take(50);
11
ответ дан Community 16 August 2018 в 14:33
поделиться
  • 1
    Какой интересный подход с алгоритмом Shuffle. Я всегда продолжал итерацию до тех пор, пока у меня не будет 50 уникальных предметов, но это явно намного худший алгоритм. Большое спасибо Хенку! – Mike Perrenoud 7 November 2013 в 14:58
IQueryable<Detail> details = myList.OrderBy(x => Guid.NewGuid()).ToList();

После этого просто пройдите по ней линейно:

var item1 = details[0];

Это позволит избежать дублирования.

2
ответ дан Jeroen Vannevel 16 August 2018 в 14:33
поделиться
  • 1
    Да, Collections.Shuffle, вы захотите сослаться на этот алгоритм или разместить свой встроенный. – Mike Perrenoud 7 November 2013 в 14:59
  • 2
    @neoistheone: heh, смущенный библиотеками Java. Хенк уже связал связанные алгоритмы, этого будет достаточно. Подход Guid определенно самый простой. – Jeroen Vannevel 7 November 2013 в 15:00
  • 3
    Знаешь, я подумал об этом, потому что, когда я сделал это в Google, я открыл библиотеку Java! – Mike Perrenoud 7 November 2013 в 15:01
  • 4
    Это позор, который еще не был встроен в библиотеки C #. Вы думаете, что это довольно простая утилита для сбора данных – Jeroen Vannevel 7 November 2013 в 15:02
  • 5
    Теперь я должен согласиться с вами в этом вопросе, что я видел алгоритм перетасовки, потому что это делает операцию O (n) с этим. Я предполагаю, что технически вы повторите еще 50 раз, но это не изменит обозначение, если я правильно запомню. – Mike Perrenoud 7 November 2013 в 15:04

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

Следующий метод сделает это за вас:

/// <summary>Randomly selects items from a sequence.</summary>
/// <typeparam name="T">The type of the items in the sequence.</typeparam>
/// <param name="sequence">The sequence from which to randomly select items.</param>
/// <param name="count">The number of items to randomly select from the sequence.</param>
/// <param name="sequenceLength">The number of items in the sequence among which to randomly select.</param>
/// <param name="rng">The random number generator to use.</param>
/// <returns>A sequence of randomly selected items.</returns>
/// <remarks>This is an O(N) algorithm (N is the sequence length).</remarks>

public static IEnumerable<T> RandomlySelectedItems<T>(IEnumerable<T> sequence, int count, int sequenceLength, System.Random rng)
{
    if (sequence == null)
    {
        throw new ArgumentNullException("sequence");
    }

    if (count < 0 || count > sequenceLength)
    {
        throw new ArgumentOutOfRangeException("count", count, "count must be between 0 and sequenceLength");
    }

    if (rng == null)
    {
        throw new ArgumentNullException("rng");
    }

    int available = sequenceLength;
    int remaining = count;
    var iterator  = sequence.GetEnumerator();

    for (int current = 0; current < sequenceLength; ++current)
    {
        iterator.MoveNext();

        if (rng.NextDouble() < remaining/(double)available)
        {
            yield return iterator.Current;
            --remaining;
        }

        --available;
    }
}

(Ключевым моментом здесь является необходимость знать с самого начала количество элементов на выбор, это немного уменьшает полезность. Но если получение счета выполняется быстро и буферизует все элементы, то потребуется слишком много памяти , это полезное решение.)


Вот еще один подход, который использует выборки резервуаров

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

Поэтому это действительно полезно, если вы заранее не знаете количество элементов на выбор (или количество элементов для выбора из очень большой).

Я бы рекомендовал просто перетасовать список в соответствии с ответом Хенка, а не делать это таким образом, но я включаю его здесь ради интереса:

// n is the number of items to randomly choose.

public static List<T> RandomlyChooseItems<T>(IEnumerable<T> items, int n, Random rng)
{
    var result = new List<T>(n);
    int index = 0;

    foreach (var item in items)
    {
        if (index < n)
        {
            result.Add(item);
        }
        else
        {
            int r = rng.Next(0, index + 1);

            if (r < n)
                result[r] = item;
        }

        ++index;
    }

    return result;
}

В качестве дополнения к ответу Хенка, это каноническая реализация алгоритма Shuffle, который он упоминает. В этом случае _rng является экземпляром Random:

/// <summary>Shuffles the specified array.</summary>
/// <typeparam name="T">The type of the array elements.</typeparam>
/// <param name="array">The array to shuffle.</param>

public void Shuffle<T>(IList<T> array)
{
    for (int n = array.Count; n > 1;)
    {
        int k = _rng.Next(n);
        --n;
        T temp = array[n];
        array[n] = array[k];
        array[k] = temp;
    }
}
4
ответ дан Matthew Watson 16 August 2018 в 14:33
поделиться
  • 1
    Я не думаю, что это даст равномерное распространение случайных предметов (что явно не указано, но, вероятно, можно предположить). Чтобы увидеть это, рассмотрим первый элемент, который можно выбрать только в качестве первого из случайных элементов, и если он не является снова кандидатом. – Chris 7 November 2013 в 14:59
  • 2
    Нет, это действительно даст ровное распространение. Это хорошо понятый алгоритм. – Matthew Watson 7 November 2013 в 15:00
  • 3
    О да. Я думал, что случайное число, которое оно сравнивало, было рассчитано один раз (поэтому, если бы оно было 0,5, оно пропустило бы первую половину списка), но на самом деле каждый раз генерирует новый. Ага. Я вижу, как это работает сейчас. – Chris 7 November 2013 в 15:03
var l = new List<string>();
l.Add("A");
l.Add("B");
l.Add("C");
l.Add("D");
l.Add("E");
l.Add("F");
l.Add("G");
l.Add("H");
l.Add("I");

var random = new Random();
var nl = l.Select(i=> new {Value=i,Index = random.Next()});

var finalList = nl.OrderBy(i=>i.Index).Take(3);
foreach(var i in finalList)
{
    Console.WriteLine(i.Value);
}
1
ответ дан Michael Mairegger 16 August 2018 в 14:33
поделиться
Random rnd = new Random();
IQueryable<Detail> details = myList.OrderBy(x => rnd.Next()).Take(50);
3
ответ дан Rik 16 August 2018 в 14:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: