полуслучайный набор подмножеств - PullRequest
3 голосов
/ 27 февраля 2011

Я пытаюсь сгенерировать полуслучайные подмножества с некоторыми ограничениями.

Вот описания переменных с примерами значений:

  • ObjCount - количество объектов (12)
  • VisibleCount (AKA SetSize) - количество объектов в наборе (6)
  • SetCount - количество наборов (12)
  • ObjAppearances - количество наборов, в которомпоявляется объект = SetCount * VisibleCount / ObjCount

Мне нужно создать определенное количество наборов (SetCount), которые следуют следующим правилам:

  1. Каждый набор представляет собой набор объектов,но ни один объект не может быть в одном наборе более одного раза.
  2. Кроме того, каждый объект должен иметь одинаковое количество наборов. Если это не делится равномерно, то наборы номеров, в которых появляется объект, могут быть отключены на 1 (некоторые объекты в 4 наборах, а другие в 5).Я постараюсь избежать этой ситуации, так что это не критично.

Оказалось, что это гораздо менее тривиально, чем я первоначально думал.Может ли кто-нибудь помочь мне с некоторым кодом / psuedocode?Было бы очень полезно найти решение обобщенной версии.

Заранее спасибо.

Редактировать: VisibleCount - установленный размер.Число раз, когда объект появляется (ObjAppearances), составляет SetCount * VisibleCount / ObjCount

Edit2: Я также должен добавить, что я хочу, чтобы наборы были довольно случайными.Если все наборы имеют последовательные объекты (например, set1: 5,6,7 set2: 3,4,5 set3: 10,11,0), решение бесполезно.Извините, что не разъяснил это.

Edit3: Вот решение, которое НЕ работает.(В C #)

static void Main(string[] args)
{
    var ObjectCount = 12;
    var SetSize = 6;
    var SetCount = 12;

    var Sets = Enumerable.Range(0, SetCount).Select(i => new List<int>()).ToArray(); // a SetCount-sized array of lists
    var ObjectAppearances = SetSize * SetCount / ObjectCount;
    var rand = new Random();

    // fill the sets
    for (int obj = 0; obj < ObjectCount; obj++)
    {
        for (int a = 0; a < ObjectAppearances; a++)
        {
            // get the collection of sets that are not full
            var nonFullSets = Sets.Where(s => s.Count < SetSize);
            // get the collection of non-full sets without obj
            var setsWithoutObj = nonFullSets.Where(s => !s.Contains(obj));
            ///////////////////////
            // Here is the problem. All of the non-full sets may already 
            // have a copy of obj
            ///////////////////////
            // choose a set at random
            var currentSetIndex = rand.Next(setsWithoutObj.Count());
            var currentSet = setsWithoutObj.ElementAt(currentSetIndex);
            // add the object
            currentSet.Add(obj);
        }
    }

    // randomize the order within each set and output each
    for (int i = 0; i < SetCount; i++)
    {
        var randomlyOrderedSet = Sets[i].OrderBy(obj => rand.Next());
        Sets[i] = new List<int>(randomlyOrderedSet);

        // output
        foreach (var obj in Sets[i])
            Console.Write(string.Format("{0}, ", obj));
        Console.WriteLine();
    }
    Console.ReadLine();
}

Вот решение - реализация ответа MizardX

static void Main(string[] args)
{
    var ObjectCount = 12;
    var SetSize = 6;
    var SetCount = 10;
    var rand = new Random();

    // make a matrix [SetCount][ObjectCount]
    var Matrix = new int[SetCount][];
    for (int s = 0; s < SetCount; s++)
        Matrix[s] = Enumerable.Repeat(0, ObjectCount).ToArray();

    // put approximately the same number of objects in each set by
    // adding sequential objects to sequential sets (not random)
    for (int s = 0; s < SetCount; s++)
    {
        var firstObject = (int)Math.Ceiling((double)s * ObjectCount / SetCount);
        for (int i = 0; i < SetSize; i++)
        {
            var o = (firstObject + i) % ObjectCount;
            Matrix[s][o] = 1;
        }
    }

    // output the result
    for (int s = 0; s < SetCount; s++)
    {
        for (int o = 0; o < ObjectCount; o++)
        {
            Console.Write(string.Format("{0}, ", Matrix[s][o]));
        }
        Console.WriteLine();
    }
    Console.WriteLine();

    // shuffle sets
    Matrix = Matrix.OrderBy(s => rand.Next()).ToArray();
    // make a new matrix for shuffle objects
    var objOrder = Enumerable.Range(0, ObjectCount).OrderBy(o => rand.Next()).ToArray();
    var MatrixSuffled = new int[SetCount][];
    for (int s = 0; s < SetCount; s++)
        MatrixSuffled[s] = Enumerable.Repeat(0, ObjectCount).ToArray();
    for (int o = 0; o < ObjectCount; o++)
    {
        var oldObj = o;
        var newObj = objOrder[o];
        for (int s = 0; s < SetCount; s++)
        {
            MatrixSuffled[s][newObj] = Matrix[s][oldObj];
        }
    }

    // check and output the result
    var objectCounters = Enumerable.Repeat(0, ObjectCount).ToArray();
    for (int s = 0; s < SetCount; s++)
    {
        var objectsInThisSet = 0;
        for (int o = 0; o < ObjectCount; o++)
        {
            objectsInThisSet += MatrixSuffled[s][o];
            objectCounters[o] += MatrixSuffled[s][o];
            Console.Write(string.Format("{0}, ", MatrixSuffled[s][o]));
        }
        Console.Write(string.Format("  {0}", objectsInThisSet));
        Console.WriteLine();
    }
    // output object count
    Console.WriteLine();
    for (int o = 0; o < ObjectCount; o++)
        Console.Write(string.Format("{0}  ", objectCounters[o]));
    Console.ReadLine();
}

Ответы [ 3 ]

1 голос
/ 27 февраля 2011

Пусть o будет количеством объектов, v будет количеством видимости, s будет количеством наборов.

  1. Для каждого объекта [повторяется o раз]
    1.1. Повторите v раз.
    1.1.1 Выбор набора случайным образом и вставка объекта - не используйте повторно набор, пока не закончится шаг 1.1.

РЕДАКТИРОВАТЬ: решение не удается, как указывает saroz. Решением может быть выбор набора с наименьшим количеством. Если существует более одного набора с таким наименьшим количеством, выберите один из них случайным образом.

1 голос
/ 27 февраля 2011
  1. Создайте матрицу ObjCount × SetCount и заполните ее единицами и нулями, чтобы каждый столбец содержал единицы VisibleCount , а каждая строка содержала(почти) равное количество единиц.Порядок не имеет значения в этой точке.
  2. Перемешать столбцы (и строки, если ObjCount не делит SetCount × VisibleCount равномерно).
  3. Для каждого столбца i , если ячейка в строке j равна 1, добавьте объект j , чтобы установить i .

Для 12 объектов, 6 наборов и 3 видимых исходная матрица может выглядеть следующим образом:

1 0 0 0 0 0
1 0 0 0 0 0
1 1 0 0 0 0
0 1 0 0 0 0
0 1 1 0 0 0
0 0 1 0 0 0
0 0 1 1 0 0
0 0 0 1 0 0
0 0 0 1 1 0
0 0 0 0 1 1
0 0 0 0 1 1
0 0 0 0 0 1

И после случайного воспроизведения:

1 0 1 0 0 0
0 0 0 0 1 0
1 1 0 0 0 0
0 0 0 1 1 0
0 1 0 0 0 0
0 0 0 1 0 0
0 0 1 0 0 0
1 0 1 0 0 0
0 0 0 1 0 0
0 0 0 0 1 1
0 1 0 0 0 1
0 0 0 0 0 1

В результате чего наборы:

{1,3,8}
{3,5,11}
{1,7,8}
{4,6,9}
{2,4,10}
{10,11,12}
0 голосов
/ 27 февраля 2011
resultSets = new Set[SetCount];  // create an array of sets to hold the results

totalObjectsPlaced = 0;
currentObjectIndex = 0;

while (totalObjectsPlaced < (ObjCount * VisibleCount)) {
  do {
    randomSet = rand(SetCount);
  } while (!resultSets[randomSet].contains(object[currentObjectIndex]));
  resultSets[randomSet].add(object[currentObjectIndex]);
  currentObjectIndex = (currentObjectIndex + 1) % ObjCount;
  totalObjectsPlaced++;
}
...