Действительно ли использование случайно и OrderBy хороший алгоритм перестановки?

Я следовал Быстрый-NHibernate очень тесно, поскольку это имеет некоторые самые потенциальные, которые я когда-либо видел в проекте.

160
задан Rodrigo Guedes 15 October 2012 в 19:39
поделиться

7 ответов

Мне не нравится такой способ перетасовки, в основном на том основании, что это O (n log n) без уважительной причины, когда легко реализовать O (n) перемешивание. Код в вопросе «работает», в основном присваивая случайный (надеюсь, уникальный!) Номер каждому элементу, а затем упорядочивая элементы в соответствии с этим номером.

Я предпочитаю вариант Дарстенфилда перетасовки Фишера-Йейтса , который меняет местами элементы.

Реализация простого метода расширения Shuffle будет в основном состоять из вызова ToList или ToArray на входе с последующим использованием существующей реализации Fisher -Йейтс. (Передайте Random в качестве параметра, чтобы сделать жизнь в целом лучше.) Существует множество реализаций вокруг ... I '

200
ответ дан 23 November 2019 в 21:31
поделиться

Этот алгоритм перемешивает, генерируя новое случайное значение для каждого значения в списке, а затем упорядочивая список по этим случайным значениям. Думайте об этом как о добавлении нового столбца в таблицу в памяти, затем заполнении его идентификаторами GUID, а затем сортировке по этому столбцу. Мне кажется, это эффективный способ (особенно с лямбда-сахаром!)

1
ответ дан 23 November 2019 в 21:31
поделиться

Слегка не связанный, но вот интересный метод (который, хотя он действительно избыточен, ДЕЙСТВИТЕЛЬНО реализован) для действительно случайного генерирования бросков костей!

Dice-O-Matic

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

2
ответ дан 23 November 2019 в 21:31
поделиться

Это, вероятно, подходит для большинства целей и почти всегда генерирует действительно случайное распределение (кроме случая, когда Random.Next () производит два одинаковых случайных целых числа).

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

Это полностью приемлемо для 99,9% приложений (если вам не нужно обрабатывать крайний случай выше). Кроме того, возражение Скита относительно его времени выполнения является обоснованным, поэтому, если вы перемешиваете длинный список, вы можете не захотеть его использовать.

7
ответ дан 23 November 2019 в 21:31
поделиться

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

Один из возможных вариантов - передача начального числа в качестве параметра в генератор случайных чисел (или генератор случайных чисел в качестве параметра), чтобы вы могли лучше контролировать и легче тестировать.

4
ответ дан 23 November 2019 в 21:31
поделиться

Это уже неоднократно повторялось. Найдите Fisher-Yates в StackOverflow.

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

static public class FisherYates
{
        //      Based on Java code from wikipedia:
        //      http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
        static public void Shuffle(int[] deck)
        {
                Random r = new Random();
                for (int n = deck.Length - 1; n > 0; --n)
                {
                        int k = r.Next(n+1);
                        int temp = deck[n];
                        deck[n] = deck[k];
                        deck[k] = temp;
                }
        }
}
4
ответ дан 23 November 2019 в 21:31
поделиться

Это основано на ответе Джона Скита.

В этом ответе массив перемешивается, а затем возвращается с использованием yield . Конечный результат состоит в том, что массив хранится в памяти на время выполнения цикла foreach, а также объекты, необходимые для итерации, но все же затраты находятся в самом начале - yield в основном представляет собой пустой цикл.

Используется этот алгоритм. много в играх, где выбираются первые три предмета, а остальные понадобятся только позже, если вообще понадобятся. Я предлагаю выдавать числа, как только они меняются местами. Это снизит начальные затраты, сохранив при этом стоимость итерации на уровне O (1) (в основном 5 операций на итерацию). Общая стоимость останется прежней, но сама перетасовка будет быстрее. В случаях, когда это называется collection.Shuffle (). ToArray () теоретически не будет иметь никакого значения, но в вышеупомянутых случаях использования он ускорит запуск. Кроме того, это сделает алгоритм полезным в случаях, когда вам нужно всего несколько уникальных элементов. Например, если вам нужно вытащить три карты из колоды из 52, вы можете вызвать deck.Shuffle (). Take (3) , и произойдет только три обмена (хотя весь массив будет иметь будет скопировано первым).

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length - 1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
        // we don't actually perform the swap, we can forget about the
        // swapped element because we already returned it.
    }

    // there is one item remaining that was not returned - we return it now
    yield return elements[0]; 
}
70
ответ дан 23 November 2019 в 21:31
поделиться
Другие вопросы по тегам:

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