我试图生成一些有限制的半随机子集。
以下是带有示例值的变量说明:
objcount-对象数(12)
VisibleCount(又名setSize)-每组对象数(6)
SetCount-集合数(12)
对象出现的集合数=SetCount * VisibleCount / ObjCount
我需要生成遵循以下规则的给定数量的集合(SetCount):
每个集合都是对象的集合,但任何对象都不能在一个集合中出现多次。
此外,每个对象应该在相同的集合中。如果偏差不均匀,则显示对象的数字集可以关闭1(有些对象位于4个集中,而其他对象位于5个集中)。我会尽量避免这种情况,所以这并不重要。
结果比我最初想象的要简单得多有人能帮我弄点密码吗?一个通用版本的解决方案也会很有帮助。
提前谢谢。
编辑:VisibleCount是设置的大小一个对象出现的次数(对象)是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();
}

最佳答案

创建一个objcount×setcount矩阵,并用1和0填充它,使每一列包含visiblecount 1,每一行包含(几乎)相等数量的1。在这一点上,秩序无关紧要。
洗牌列(和行,如果objcount不平均划分setcount×visiblecount)。
对于每一列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}

10-08 07:13