Как перемешать несколько связанных массивов? - PullRequest
1 голос
/ 13 июня 2011

У меня есть кое-что необычное, что мне нужно сделать. Мне интересно, может ли кто-нибудь придумать способ сделать изменения, которые мне нужны. Что у меня есть

public class Report
    { 
        public string[] Text { get; set; }
        public string[] Image { get; set; }
        public string[] Explanation { get; set; }
    }

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

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

Report.Text[0] = "text0";
Report.Text[1] = "text1";
Report.Text[2] = "text2";
Report.Image[0] = "img0";
Report.Image[1] = "img1";
Report.Image[2] = "img2";
Report.Explanation[0] = "exp0";
Report.Explanation[1] = "exp1";
Report.Explanation[2] = "exp2";

затем после сортировки

Report.Text[0] = "text2";
Report.Text[1] = "text0";
Report.Text[2] = "text1";
Report.Image[0] = "img2";
Report.Image[1] = "img0";
Report.Image[2] = "img1";
Report.Explanation[0] = "exp2";
Report.Explanation[1] = "exp0";
Report.Explanation[2] = "exp1";

Кто-нибудь может придумать простой способ сделать это? Все, что я могу думать о том, что мне нужно создать новый временный объект того же размера и сделать какой-то обмен. Но я не уверен, как рандомизировать. Причина, по которой я спрашиваю, на тот случай, если у кого-то была такая потребность в прошлом.

Ответы [ 5 ]

5 голосов
/ 13 июня 2011

Я бы настоятельно рекомендовал бы выполнить рефакторинг этого, чтобы создать отдельный класс для инкапсуляции кортежа { Text, Image, Explanation }.В этот момент код станет чище, и будет просто изменить порядок значений.Черт, вам может даже не нужен тип Report в этот момент ... вы можете просто иметь List<ReportItem> или что-то еще.Вам нужен только отдельный тип Report, если вы хотите добавить дополнительное поведение или данные, чтобы связать вещи вместе.

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

Если у вас , то у есть вопрос о перетасовке одной коллекции, вероятно, самый простой подход - это модифицированный Fisher-Yates shuffle .Вы могли бы сделать это и с несколькими массивами, но это было бы нехорошо - и должно было бы быть специфичным для Report ... тогда как вы могли бы легко написать универсальную реализацию на основе Fisher-Yatesна IList<T>.Если вы выполняете поиск по переполнению стека, вы легко сможете найти несколько существующих реализаций:)

1 голос
/ 13 июня 2011

Если вы решите изменить свой класс на следующий:

public class Report
{ 
    public string Text { get; set; }
    public string Image { get; set; }
    public string Explanation { get; set; }
}

Затем вы можете сделать это, используя метод расширения:

( См. Ответ на этот вопрос SO )

Тогда назовите это так:

List<Report> reports = new List<Report> { /* create list of reports */ }
Random rnd = new Random();
foreach (Report r in reports.Shuffle(rnd)) {
    /* do something with each report */
}
0 голосов
/ 13 июня 2011

Я не уверен, что мне нравится это направление, но ...

Чтобы делать в точности то, что вы просите (закон, а не дух закона), вам нужно будет добавить дополнительные массивы и переместить элементы. Кроме того, для каждого массива вам потребуется список или аналогичный элемент для хранения элементов, которые вы уже случайно перетянули. После этого все просто. Используйте класс Random для создания случайных чисел, проверьте, не был ли элемент уже перемещен (используя Список), если не сохраните результат в новом массиве / списке, добавьте значение в свой Список, чтобы убедиться, что вы не перемещаете его пункт дважды. Как только все будет перемещено, установите этот новый массив в старый массив.

Теперь, что является бизнес-причиной для рандомизации? Это может повлиять на то, является ли это хорошей идеей.

ДОБАВЛЕНО:

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

public class Report {
   public string Text { get; set; }
   public string Image { get; set; } 
   public string Explanation { get; set; } 
} 

Вот один из типов "грязных":

    private static SortedList<int, Report> SortRandomly(List<Report> reports)
    {
        Random rnd = new Random((int)DateTime.Now.Ticks);
        List<int> usedNumbers = new List<int>();
        SortedList<int, Report> sortedReports = new SortedList<int, Report>();
        int maxValue = reports.Count;

        foreach(Report report in reports)
        {
            bool finished = false;
            int randomNumber = 0;

            //Get unique random (refactor out?)
            while(!finished)
            {

                randomNumber = rnd.Next(0, maxValue);

                if(!usedNumbers.Contains(randomNumber))
                {
                    finished = true;
                    usedNumbers.Add(randomNumber);
                }
            }

            sortedReports.Add(randomNumber, report);
        }

        return sortedReports;
    }

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

    private static List<Report> SortRandomly(List<Report> reports)
    {
        Random rnd = new Random((int)DateTime.Now.Ticks);
        List<Report> outputList = new List<Report>();
        List<int> usedNumbers = new List<int>();
        int maxValue = reports.Count-1;

        while(outputList.Count < reports.Count)
        {
            int randomNumber = rnd.Next(0, maxValue);

            if(!usedNumbers.Contains(randomNumber))
            {
                outputList.Add(reports[randomNumber]);
            }
        }

        return outputList;
    }

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

0 голосов
/ 13 июня 2011

Вот мое решение

class StringWrapper
    {
        public int Index;
        public string Str;
    }

    public string[] MixArray(string[] array)
    {
        Random random = new Random();
        StringWrapper[] wrappedArray = WrapArray(array);

        for (int i = 0; i < wrappedArray.Length; i++)
        {
            int randomIndex = random.Next(0, wrappedArray.Length - 1);
            wrappedArray[i].Index = randomIndex;
        }

        Array.Sort(wrappedArray, (str1, str2) => str1.Index.CompareTo(str2.Index));
        return wrappedArray.Select(wrappedStr => wrappedStr.Str).ToArray();
    }

    private StringWrapper[] WrapArray(string[] array)
    {
        int i = 0;
        return array.Select(str => new StringWrapper {Index = ++i, Str = str}).ToArray();
    }

Затем вы можете вызывать MixArray для каждого объекта Report для каждого свойства, которое вы хотите использовать для рандомизации.

0 голосов
/ 13 июня 2011

Почему бы вам не создать класс

public class Report
{
    public string Text { get; set; }
    public string Image { get; set; }
    public string Explanation { get; set; }
}

, а затем создайте список этих объектов и управляйте им через свойства списка:

IList<Report> yourList = new List<Report>()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...