Список в случайном порядке ‹T›

Возможный дубликат:
Случайный выбор списка ‹T› в C #

У меня есть список, который содержит многие тысячи FilePath для местоположений аудиофайлов, и мне было интересно, какой способ «перетасовать» список был бы наиболее эффективным?

Любая помощь приветствуется :)

Спасибо


person Community    schedule 20.02.2010    source источник
comment
См. Это сообщение: http://stackoverflow.com/questions/273313/randomize-a-listt-in-c   -  person dugas    schedule 20.02.2010
comment
Вам действительно нужно наиболее эффективное возможное решение или вы хотите приемлемо эффективное решение? Потому что есть алгоритмы, которые даже более эффективны, чем предоставил Фишер-Йейтс, вы готовы отказаться от некоторых хороших свойств, таких как отсутствие предвзятости. (Не то чтобы Фишер-Йейтс, как реализовано ниже, беспристрастен; это глубоко предвзято.)   -  person Eric Lippert    schedule 20.02.2010
comment
@Eric: Фишер-Йейтс беспристрастен. Как вы заметили, приведенная ниже реализация неверна. Конечно, есть более эффективные реализации, если вы хотите иметь предвзятость. Например, вообще ничего не делать. Я действительно не понимаю твою точку зрения. OP ничего не указал, и разумно (IMO) предположить, что они ищут равномерное перемешивание.   -  person    schedule 20.02.2010
comment
Это действительно разумно? Рассматриваемый алгоритм перемешивания предназначен для медиафайлов. Можно смещать перемешивание в сторону более частого повторения песен с более высоким рейтингом.   -  person Eric Lippert    schedule 21.02.2010
comment
@Eric: Что является разумным или нет, полностью зависит от контекста j-t-s (которого у нас нет), но, учитывая информацию в вопросе, да, я бы сказал, что разумно предположить, что это единообразно.   -  person    schedule 21.02.2010
comment
@Eric: Я ищу ‹i› наиболее ‹/i› эффективного решения, хотя приемлемо эффективное решение тоже было бы неплохо. В настоящее время у одного из моих пользователей есть библиотека из 500 000 аудиофайлов на своем компьютере 8-летней давности. И я полагаю, что если есть один из них, то, вероятно, их будет больше, и я хотел бы, чтобы все было как можно быстрее.   -  person    schedule 21.02.2010
comment
Тогда я бы решил вашу проблему, решив другую задачу. Зачем нужно воспроизводить в случайном порядке полмиллиона файлов? Пользователь никогда, никогда не дойдет до последнего файла в случайном порядке, даже если он будет сидеть там и нажимать следующий день каждый день в течение нескольких месяцев. То есть зачем вообще предварительно вычислять весь перетасованный порядок ? Выбери несколько сотен наугад (без замены) и назови это хорошо. Это должно быть не только быстрее, но и намного эффективнее с точки зрения памяти, чем выделение массива из пятисот тысяч имен файлов и перетасовка всего массива.   -  person Eric Lippert    schedule 21.02.2010
comment
@ j-t-s: Согласен с Эриком: попытки перетасовать файлы для такого огромного размера кажутся бессмысленными (и очень неэффективными). Предложение, отличное от предложения Эрика: вы можете попробовать сохранить список, скажем, из 10 (или 50) последних проигранных файлов. Для следующего файла вы можете сгенерировать случайное число / файл (от 1 до 1/2 миллиона), и если он входит в число последних 10 (или 50) воспроизведенных, попробуйте снова получить случайное число. Этого должно хватить для всех практических целей.   -  person    schedule 21.02.2010


Ответы (4)


Fisher-Yates Shuffle или, как его еще называют, Knuth shuffle.

person Community    schedule 20.02.2010
comment
... что является O (n), поэтому вы не можете найти лучшего, чем это. - person Guffa; 20.02.2010
comment
... Я просто проголосовал за вас, потому что мне понравился ваш ответ :) Не знаю, почему он был отклонен? - person ; 20.02.2010
comment
Кстати, для более быстрого перемешивания я бы посоветовал вам перемешать список / массив целых чисел (используя любой выбранный вами метод) и использовать этот перемешанный список / массив в качестве индекса в списке имен файлов. Обмен имен файлов может оказаться узким местом. - person ; 20.02.2010

Вот простая (но эффективная) реализация перетасовки Фишера-Йейтса / Кнута:

Random rnd = new Random();
for (int i = files.Length; i > 1; i--) {
  int pos = rnd.Next(i);
  var x = files[i - 1];
  files[i - 1] = files[pos];
  files[pos] = x;
}

Или небольшая вариация:

Random rnd = new Random();
for (int i = 1; i < files.Length; i++) {
  int pos = rnd.Next(i + 1);
  var x = files[i];
  files[i] = files[pos];
  files[pos] = x;
}

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

Я провел небольшой тест производительности, перетасовывая миллион элементов по тысяче раз каждый, используя этот метод и принятый в настоящее время ответ (LINQ OrderBy), и это примерно в 15 раз (!) Быстрее.

person Guffa    schedule 20.02.2010
comment
Вы путаете асимптотическую оценку с эффективностью. Эффективность определяется как ресурсы, потребляемые на единицу выполненной работы. Асимптотическая граница описывает, как потребляемые ресурсы увеличиваются по мере увеличения размера проблемы. Алгоритм поиска подстроки длины m в строке длины n в классе System.String составляет O (nm), но он намного эффективнее на типичном проблем, чем O (n + m) алгоритмов, которые мы могли бы реализовать. Чтобы определить эффективность, вы должны учитывать вероятные случаи, а не асимптотические границы. - person Eric Lippert; 20.02.2010
comment
Я также отмечаю, что ваша реализация Фишера-Йейтса имеет предвзятость; он не производит всех возможных перетасовок с равной вероятностью. Вероятно, это не проблема для алгоритма перетасовки музыки, но это проблема, если вы использовали его для перетасовки колоды карт для игры в покер; давая мне руку, я мог быстро определить, что у всех было в руках. - person Eric Lippert; 20.02.2010
comment
@Eric: Как вы думаете, почему реализация имеет предвзятость? Это дает каждому элементу одинаковый шанс оказаться на каждой позиции в списке. Также я проверил реализацию, выполнив миллионы перетасовок, и нет заметной предвзятости. - person Guffa; 20.02.2010
comment
Пусть x будет количеством возможных случайных порядков, созданных Random. Пусть y будет количеством возможных порядков тасования. Я думаю, вы обнаружите, что если вы выясните, что такое x и y, x будет намного меньше, чем y в вашей реализации, и, следовательно, перетасовка смещена. - person Eric Lippert; 20.02.2010
comment
@Eric: Для списка из n элементов x = n! и y = n !, поэтому я не понимаю, как x может быть намного меньше y. - person Guffa; 20.02.2010
comment
@Guffa: Эрик прав, ваша реализация - это не Фишер Йейтс, хотя и выглядит так. Начать нужно с другого конца. - person ; 20.02.2010
comment
@Moron: Вы правы, что на самом деле это не реализация Фишера Йейтса, но она отлично работает. В каком смысле вы имеете в виду, что Эрик прав? (Я также добавил алгоритм Фишера Йейтса.) - person Guffa; 21.02.2010
comment
Предвзятость исходит не от алгоритма. Смещение происходит из источника случайности. x на самом деле 2 ^ 32: количество возможных семян. И поскольку некоторые из этих семян гораздо более вероятны, чем другие - поскольку исходное значение основано на часах, и когда люди выбирают запуск программного обеспечения, оно не распределяется равномерно во времени - это еще больше предвзятости. - person Eric Lippert; 21.02.2010
comment
@Eric: О, я понимаю, что вы имеете в виду, класс Random имеет верхний предел 2 ^ 32 различных случайных последовательностей. Ну, по крайней мере, он менее предвзят, чем метод сортировки, поскольку он имеет тот же предел в генераторе случайных чисел, а также имеет предвзятость для повторяющихся случайных значений ... - person Guffa; 21.02.2010
comment
@Guffa: Извините, я неправильно прочитал комментарий Эрика. Все, что я хотел сказать, это не Фишер Йейтс. Что касается проблемы смещения: как вы ее реализовали, мы можем доказать, что если случайный генератор является однородным, то произвольное перемешивание будет однородным (или «несмещенным»). Извините за путаницу. - person ; 21.02.2010

Я добавил решение Джона Скита из этого вопроса в мою библиотеку расширений. Я реализовал методы, которые используют внешний генератор случайных чисел и создают его с использованием реализации по умолчанию (Random).

person tvanfosson    schedule 20.02.2010

person    schedule
comment
Некоторые алгоритмы генерации GUID генерируют монотонные значения, поэтому это может не дать случайных результатов. Однако что-то подобное с использованием другого источника случайности (возможно, случайного) будет работать. - person heneryville; 04.10.2012