Лучший способ рандомизировать массив с .NET.

Каков наилучший способ рандомизации массива строк с .NET? Мой массив содержит около 500 строк, и я хотел бы создать новый Array с теми же строками, но в случайном порядке.

Пожалуйста, включите в свой ответ пример С#.

Ответ 1

Если вы используете .NET 3.5, вы можете использовать следующую крутость IEnumerable (VB.NET, а не С#, но идея должна быть ясной...):

Random rnd=new Random();
string[] MyRandomArray = MyArray.OrderBy(x => rnd.Next()).ToArray();    

Отредактируйте: ОК и вот соответствующий код VB.NET:

Dim rnd As New System.Random
Dim MyRandomArray = MyArray.OrderBy(Function() rnd.Next()).ToArray()

Второе редактирование в ответ на замечания о том, что System.Random "не является потокобезопасным" и "подходит только для игрушечных приложений" из-за возврата последовательности, основанной на времени: как используется в моем примере, функция Random() является полностью поточно-ориентированной, если только вы позволяете повторно ввести процедуру, в которой вы рандомизируете массив, и в этом случае вам все равно понадобится что-то вроде lock (MyRandomArray) чтобы не повредить ваши данные, что также защитит rnd.

Также следует понимать, что System.Random как источник энтропии не очень силен. Как отмечено в документации MSDN, вы должны использовать что-то, полученное из System.Security.Cryptography.RandomNumberGenerator если вы делаете что-то, связанное с безопасностью. Например:

using System.Security.Cryptography;

...

RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider();
string[] MyRandomArray = MyArray.OrderBy(x => GetNextInt32(rnd)).ToArray();

...

static int GetNextInt32(RNGCryptoServiceProvider rnd)
    {
        byte[] randomInt = new byte[4];
        rnd.GetBytes(randomInt);
        return Convert.ToInt32(randomInt[0]);
    }

Ответ 2

Следующая реализация использует алгоритм Фишера-Йейтса AKA Knuth Shuffle. Он выполняется за O (n) времени и тасует на месте, поэтому лучше работает, чем метод "сортировки по случайному принципу", хотя в нем больше строк кода. Смотрите здесь для некоторых сравнительных измерений производительности. Я использовал System.Random, что хорошо для некриптографических целей. *

static class RandomExtensions
{
    public static void Shuffle<T> (this Random rng, T[] array)
    {
        int n = array.Length;
        while (n > 1) 
        {
            int k = rng.Next(n--);
            T temp = array[n];
            array[n] = array[k];
            array[k] = temp;
        }
    }
}

Использование:

var array = new int[] {1, 2, 3, 4};
var rng = new Random();
rng.Shuffle(array);
rng.Shuffle(array); // different order from first call to Shuffle

* Для более длинных массивов, чтобы сделать (чрезвычайно большое) число перестановок одинаково вероятным, необходимо запустить генератор псевдослучайных чисел (PRNG) через множество итераций для каждого обмена, чтобы произвести достаточно энтропии. Для массива из 500 элементов только очень малая часть из возможных 500! Перестановки можно будет получить с помощью PRNG. Тем не менее, алгоритм Фишера-Йейтса беспристрастен, и поэтому перемешивание будет таким же хорошим, как и используемый вами ГСЧ.

Ответ 3

Вы ищете алгоритм перетасовки, правильно?

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

Немой путь

  • Создайте дубликат вашего первого массива, но тег каждой строки должен содержать случайное число.
  • Сортировка повторяющегося массива относительно случайного числа.

Этот алгоритм работает хорошо, но убедитесь, что ваш генератор случайных чисел вряд ли отметит две строки с тем же номером. Из-за так называемого Day Paradox это происходит чаще, чем вы могли ожидать. Его временная сложность - O (n log n).

Умный способ

Я опишу это как рекурсивный алгоритм:

Чтобы перетасовать массив размером n (индексы в диапазоне [0..n-1]):

если n = 0
  • ничего не делать
если n > 0
  • (рекурсивный шаг) перетасовать первые n-1 элементы массива
  • выберите случайный индекс, x, в диапазоне [0..n-1]
  • замените элемент на индекс n-1 на элемент с индексом x

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

Сложность времени - O (n).

Ответ 4

Этот алгоритм прост, но не эффективен, O (N 2). Все алгоритмы "упорядочиваются", как правило, O (N log N). Это, вероятно, не делает различий ниже сотен тысяч элементов, но для больших списков.

var stringlist = ... // add your values to stringlist

var r = new Random();

var res = new List<string>(stringlist.Count);

while (stringlist.Count >0)
{
   var i = r.Next(stringlist.Count);
   res.Add(stringlist[i]);
   stringlist.RemoveAt(i);
}

Причина, по которой это O (N 2), является тонкой: List.RemoveAt() - это O (N), если вы не удалите их с конца.

Ответ 5

Вы также можете сделать метод расширения из Matt Howells. Пример.

   namespace System
    {
        public static class MSSystemExtenstions
        {
            private static Random rng = new Random();
            public static void Shuffle<T>(this T[] array)
            {
                rng = new Random();
                int n = array.Length;
                while (n > 1)
                {
                    int k = rng.Next(n);
                    n--;
                    T temp = array[n];
                    array[n] = array[k];
                    array[k] = temp;
                }
            }
        }
    }

Тогда вы можете просто использовать его, например:

        string[] names = new string[] {
                "Aaron Moline1", 
                "Aaron Moline2", 
                "Aaron Moline3", 
                "Aaron Moline4", 
                "Aaron Moline5", 
                "Aaron Moline6", 
                "Aaron Moline7", 
                "Aaron Moline8", 
                "Aaron Moline9", 
            };
        names.Shuffle<string>();

Ответ 6

Рандомизация массива интенсивна, так как вам приходится перемещаться по цепочке строк. Почему бы просто не случайно прочитать из массива? В худшем случае вы даже можете создать класс-оболочку с помощью getNextString(). Если вам действительно нужно создать случайный массив, вы можете сделать что-то вроде

for i = 0 -> i= array.length * 5
   swap two strings in random places

. * 5 произвольно.

Ответ 7

Просто размышляя над моей головой, вы можете сделать это:

public string[] Randomize(string[] input)
{
  List<string> inputList = input.ToList();
  string[] output = new string[input.Length];
  Random randomizer = new Random();
  int i = 0;

  while (inputList.Count > 0)
  {
    int index = r.Next(inputList.Count);
    output[i++] = inputList[index];
    inputList.RemoveAt(index);
  }

  return (output);
}

Ответ 8

Генерировать массив случайных float или int с одинаковой длиной. Сортируйте этот массив и выполните соответствующие свопы в целевом массиве.

Это дает действительно независимый вид.

Ответ 9

Random r = new Random();
List<string> list = new List(originalArray);
List<string> randomStrings = new List();

while(list.Count > 0)
{
int i = r.Random(list.Count);
randomStrings.Add(list[i]);
list.RemoveAt(i);
}

Ответ 10

Jacco, ваше решение, созданное обычным IComparer, небезопасно. Процедуры Сортировка требуют, чтобы компаратор соответствовал нескольким требованиям для правильной работы. Первая из них - последовательность. Если компаратор вызывается на одной и той же паре объектов, он должен всегда возвращать тот же результат. (сравнение также должно быть транзитивным).

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

Что касается решений, которые связывают случайное числовое значение с каждой записью, а затем сортируют по этому значению, это приводит к присущему смещению вывода, поскольку в любое время двум элементам присваивается одно и то же числовое значение, случайность вывода будет быть скомпрометированным. (В "стабильной" процедуре сортировки, в зависимости от того, что будет первым на входе, будет первым в выходе. Array.Sort нестабильна, но по-прежнему существует предвзятость, основанная на разбиении, выполняемом алгоритмом Quicksort).

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

Для перетасовки списка песен нет проблем с использованием затравочного PRNG (например, System.Random). Для покерного сайта это даже не вариант, и вам нужно подумать о проблеме намного сложнее, чем кто-либо собирается сделать для вас в stackoverflow. (использование криптографического RNG - это только начало, вам нужно убедиться, что ваш алгоритм не вводит предубеждение, что у вас есть достаточные источники энтропии, и что вы не подвергаете никакому внутреннему состоянию, которое могло бы нарушить последующую случайность).

Ответ 11

Это сообщение уже довольно хорошо ответило - используйте реализацию Durstenfeld для Shuffle Fisher-Yates для быстрого и непредвзятого результата. Там даже были некоторые реализации, но я заметил, что некоторые из них действительно неверны.

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

Ответ 12

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

public static class EnumerableExtensions
{
    static readonly RNGCryptoServiceProvider RngCryptoServiceProvider = new RNGCryptoServiceProvider();
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> enumerable)
    {
        var randomIntegerBuffer = new byte[4];
        Func<int> rand = () =>
                             {
                                 RngCryptoServiceProvider.GetBytes(randomIntegerBuffer);
                                 return BitConverter.ToInt32(randomIntegerBuffer, 0);
                             };
        return from item in enumerable
               let rec = new {item, rnd = rand()}
               orderby rec.rnd
               select rec.item;
    }
}

Shuffle() является расширением для любого IEnumerable, поэтому получение, скажем, чисел от 0 до 1000 в случайном порядке в списке может быть выполнено с помощью

Enumerable.Range(0,1000).Shuffle().ToList()

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

Ответ 13

Вам не нужны сложные алгоритмы.

Только одна простая строка:

Random random = new Random();
array.ToList().Sort((x, y) => random.Next(-1, 1)).ToArray();

Обратите внимание, что сначала нужно преобразовать Array в List, если вы не используете List в первую очередь.

Кроме того, помните, что это не эффективно для очень больших массивов! В противном случае он чист и прост.

Ответ 14

Это полноценное рабочее консольное решение на основе приведенного здесь примера:

class Program
{
    static string[] words1 = new string[] { "brown", "jumped", "the", "fox", "quick" };

    static void Main()
    {
        var result = Shuffle(words1);
        foreach (var i in result)
        {
            Console.Write(i + " ");
        }
        Console.ReadKey();
    }

   static string[] Shuffle(string[] wordArray) {
        Random random = new Random();
        for (int i = wordArray.Length - 1; i > 0; i--)
        {
            int swapIndex = random.Next(i + 1);
            string temp = wordArray[i];
            wordArray[i] = wordArray[swapIndex];
            wordArray[swapIndex] = temp;
        }
        return wordArray;
    }         
}

Ответ 15

Этот код перемещает числа в массиве.

using System;

// ...
    static void Main(string[] args)
    {
        Console.ForegroundColor = ConsoleColor.Cyan;
        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        Shuffle(numbers);

        for (int i = 0; i < numbers.Length; i++)
            Console.Write(numbers[i] + (i < numbers.Length - 1 ? ", " : null));
        Console.WriteLine();

        string[] words = { "this", "is", "a", "string", "of", "words" };
        Shuffle(words);

        for (int i = 0; i < words.Length; i++)
            Console.Write(words[i] + (i < words.Length - 1 ? ", " : null));
        Console.WriteLine();

        Console.ForegroundColor = ConsoleColor.Gray;
        Console.Write("Press any key to quit . . . ");
        Console.ReadKey(true);
    }

    static void Shuffle<T>(T[] array)
    {
        Random random = new Random();

        for (int i = 0; i < array.Length; i++)
        {
            T temporary = array[i];
            int intrandom = random.Next(array.Length);
            array[i] = array[intrandom];
            array[intrandom] = temporary;
        }
    }

Ответ 16

        int[] numbers = {0,1,2,3,4,5,6,7,8,9};
        List<int> numList = new List<int>();
        numList.AddRange(numbers);

        Console.WriteLine("Original Order");
        for (int i = 0; i < numList.Count; i++)
        {
            Console.Write(String.Format("{0} ",numList[i]));
        }

        Random random = new Random();
        Console.WriteLine("\n\nRandom Order");
        for (int i = 0; i < numList.Capacity; i++)
        {
            int randomIndex = random.Next(numList.Count);
            Console.Write(String.Format("{0} ", numList[randomIndex]));
            numList.RemoveAt(randomIndex);
        }
        Console.ReadLine();

Ответ 17

Алгоритм Фишера-Йейта

Лучше использовать список, а не массив. Поскольку массив не изменен, тогда как список изменен.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// This class contains Extension methods .
/// </summary>
public static  class ExtensionMethods  {

    /// <summary>
    /// This method take an list as an input and return the same list in a shuffle condition.
    /// <para>https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle</para>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="inputList"></param>
    /// <returns>A genaric list in shuffled manner</returns>
    public static List<T> Shuffle<T>(List<T> inputList)
    {

        //We will shuffle the list using Fisher–Yates shuffle methos .
        //First we will create a list that will be returned .
        var shuffleList = new List<T>();
        //We are using system random to generate the random value .
        var rand = new System.Random();
        //Iterate over it .
        while (inputList.Count > 1)
        {
            //Debug .
            Debug.Log("Current Size of the list"+ inputList.Count);
            //random index.
            var indexrand = rand.Next(1, inputList.Count);
            //Debug .
            Debug.Log("Random value that is generated for the index " + indexrand);
            //access the item .
            var swapItem = inputList[indexrand];
            //Debug
            Debug.Log("Value At that position "+ swapItem);
            //Swap this item with the last item .
            //Set the last item to the random position.
            Debug.Log("Access the value of random position " + inputList[indexrand]);
            //Indexing start from 0 .
            inputList[indexrand] = inputList[inputList.Count - 1];
            //Debug.
            Debug.Log("Set the list end value to that position "+ indexrand +"--->"+ inputList[inputList.Count - 1]);
            //Set the random position value to the last position .
            inputList[inputList.Count - 1] = swapItem;
            //Remove the last position of the end ..
            inputList.RemoveAt(inputList.Count - 1);
            //Add it to the shuffle list .
            shuffleList.Add(swapItem);
        }

       //Print You List
        for (var index = 0; index < shuffleList.Count; index++)
        {
            var item = shuffleList[index];

            Debug.Log("Item at =  " + index + "\t Value =" + item);
        }

        //Retun,
        return shuffleList;
    }

}

Ответ 18

Здесь простой способ использования OLINQ:

// Input array
List<String> lst = new List<string>();
for (int i = 0; i < 500; i += 1) lst.Add(i.ToString());

// Output array
List<String> lstRandom = new List<string>();

// Randomize
Random rnd = new Random();
lstRandom.AddRange(from s in lst orderby rnd.Next(100) select s);

Ответ 19

Вот мой алгоритм. Мы хотим рандомизировать массив с именем полюс "

List<int> vylosovane = new List<int>();
List<string> docasny = new List<string>();
int a = 0;
for (int i = 0; i < pole.Length; i++)
    {
          a = nahoda.Next(0, pole.Length);
          if (vylosovane.Contains(a))
              i--;
          else
          {
              docasny.Add(pole[a]);
              vylosovane.Add(a);
          }
    }
pole = docasny.ToArray();

Ответ 20

private ArrayList ShuffleArrayList(ArrayList source)
{
    ArrayList sortedList = new ArrayList();
    Random generator = new Random();

    while (source.Count > 0)
    {
        int position = generator.Next(source.Count);
        sortedList.Add(source[position]);
        source.RemoveAt(position);
    }  
    return sortedList;
}