半随机的子集

时间:2011-02-27 01:56:21

标签: algorithm random set

我正在尝试生成一些有一些限制的半随机子集。

以下是带有示例值的变量描述:

  • ObjCount - 对象数量(12)
  • VisibleCount(AKA SetSize) - 每组对象的数量(6)
  • SetCount - 套数(12)
  • ObjAppearances - 对象出现的集合数量= SetCount * VisibleCount / ObjCount

我需要按照这些规则生成给定数量的集合(SetCount):

  1. 每个集合都是一个对象的集合,但没有一个对象可以在一个集合中多次出现。
  2. 此外,每个对象应该具有相同数量的集合。 如果它没有均匀分布,则对象出现的数字集可以偏离1(某些对象分为4组,其他对象分为5组)。我会尽量避免这种情况,所以这并不重要。
  3. 事实证明,这比我最初的想法要小得多。有人可以帮我一些代码/伪代码吗?广义版本的解决方案也非常有用。

    提前致谢。

    修改:VisibleCount是设置尺寸。对象出现的次数(ObjAppearances)为SetCount * VisibleCount / ObjCount

    Edit2:我还应该补充一点,我希望这些集合相当随机。如果集合都具有顺序对象(例如set1:5,6,7 set2:3,4,5 set3:10,11,0),则该解决方案无用。很抱歉没有说清楚。

    编辑3:这是一个无效的解决方案。 (在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 个答案:

答案 0 :(得分:1)

o为对象数量,v为可见度,s为集合数。

  1. 对于每个对象[重复o次]
      1.1。重复v次          1.1.1随机选择一个集合并插入对象 - 在步骤1.1结束之前不要重复使用该集合。
  2. 编辑:解决方案失败,因为萨罗兹表示。修复可能是选择最少计数的集合。如果存在多个具有最少计数的集合,则随机选择其中一个。

答案 1 :(得分:1)

  1. 创建 ObjCount × SetCount 矩阵,并用1和0填充它,以便每列包含 VisibleCount ,并且每行包含一个(几乎)相同数量的。此时订单无关紧要。
  2. 对列进行随机播放(如果 ObjCount 不会将 SetCount × VisibleCount 均匀划分),则对行进行随机播放。
  3. 对于每列 i ,如果 j 行的单元格等于1,请添加对象 j 以设置 i
  4. 对于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}
    

答案 2 :(得分:0)

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++;
}