构建组合矩阵

时间:2013-02-27 00:25:17

标签: c# algorithm matrix permutation combinations

我确信这已被问了一百万次,但是当我搜索所有的例子都不太合适时,我想我还是应该问它。

我有两个数组,每个数组总共包含6个项目。例如:

string[] Colors=
    new string[] { "red", "orange", "yellow", "green", "blue", "purple" };

string[] Foods=
    new string[] { "fruit", "grain", "dairy", "meat", "sweet", "vegetable" };

在这两个阵列之间,有36种可能的组合(例如“红色水果”,“红色颗粒”)。

现在我需要进一步将这些组合成六组唯一值。

例如:

meal[0]=
    new Pair[] { 
        new Pair { One="red", Two="fruit" }, 
        new Pair { One="orange", Two="grain" }, 
        new Pair { One="yellow", Two="dairy" }, 
        new Pair { One="green", Two="meat" }, 
        new Pair { One="blue", Two="sweet" }, 
        new Pair { One="purple", Two="vegetable" } 
    };

在哪里用餐

Pair[][] meal;

我的“用餐”列表中没有可重复的元素。因此,只有一个“红色”项目和一个“肉”项目等。

我可以根据前两个数组轻松创建对,但我在如何最好地将它们组合成唯一组合方面做了一个空白。

3 个答案:

答案 0 :(得分:5)

好的,你想要一个包含所有720个可能序列的序列。这有点棘手,但可以做到。

基本思路与我之前的答案相同。在那个答案中我们:

  • 随机生成排列
  • 使用未计算的第一个数组
  • 压缩置换的第二个数组
  • 从查询中生成了一个数组

现在我们会做同样的事情,除了不是随机产生一个排列,我们将产生所有的排列。

首先获取此库:

http://www.codeproject.com/Articles/26050/Permutations-Combinations-and-Variations-using-C-G

好的,我们需要完成六个项目的所有排列:

Permutations<string> permutations = new Permutations<string>(foods);

我们想对每个排列做什么?我们已经知道了。我们想首先用颜色数组压缩它,将它变成一对数组,然后我们将它们变成一个数组。相反,让我们把它变成List<Pair>因为,相信我,它会更容易。

IEnumerable<List<Pair>> query = 
    from permutation in permutations
    select colors.Zip(permutation, (color, food)=>new Pair(color, food)).ToList();

现在我们可以将该查询转换为结果列表;

List<List<Pair>> results = query.ToList();

我们已经完成了。我们有一个包含720个项目的列表。每个项目都是一个包含6对的列表。

显然,繁重的工作是由图书馆代码完成的;放在它上面的查询非常简单。

(已经写了一篇关于在LINQ中生成排列的方法的博客文章;我可能会以此为例!)

答案 1 :(得分:4)

有720种可能的组合可满足您的需求。从您的问题中不清楚您是否要枚举所有720 随机选择或什么。我将假设后者。

更新:根据评论,这个假设是不正确的。我会开始一个新的答案。


首先,产生第二个数组的排列。你可以用Fischer-Yates-Knuth shuffle就地做到这一点;有很多关于如何在StackOverflow上执行此操作的示例。或者,您可以使用随机密钥对LINQ进行排列。

即使项目数量很大,前一种技术也很快,但会改变现有的数组。第二种技术较慢,特别是如果物品的数量非常大,则不是。

人们使用第二种技术犯下的最常见错误是对guid进行排序。 Guids保证唯一,不保证随机

无论如何,产生一个查询,当执行时,它会置换第二个数组:

Random random = new Random();
IEnumerable<string> shuffled = from food in foods 
                               orderby random.NextDouble() 
                               select food;

其他一些警告:

  • 请记住,查询表达式的结果是查询,而不是一组结果。直到你真正把东西变成另一端的数组才会发生排列。
  • 如果你在同一毫秒内创建两个Random实例,你可以得到相同的序列。
  • 随机是伪随机的,不是真正随机的。
  • 随机不是线程安全。

现在,您可以将置换序列压缩连接到第一个数组:

IEnumerable<Pair> results = colors.Zip(shuffled, (color, food)=>new Pair(color, food));

同样,这仍然是一个表示将两个序列压缩在一起的动作的查询。除了构建一些查询之外,什么也没发生。

最后,将其变成一个数组。这实际上执行了查询。

Pair[] finalResults = results.ToArray();

轻松自负。

答案 2 :(得分:1)

根据要求,我将具体说明如何查看有关排序的问题。我知道,由于C#是一种更高级别的语言,因此可以使用大量快速简便的库和对象将其减少到最少的代码。这个答案实际上是试图通过实现排序逻辑来解决问题。

当我最初阅读这个问题时,我被提醒要排序一副牌。这两个数组非常类似于诉讼数组和面值数组。由于解决shuffle的一种方法是随机化数组,然后选择两者结合的卡,你可以在这里应用相同的逻辑。

排序为可能的解决方案

Fisher-Yates 排序算法实质上循环遍历使用随机索引交换当前索引的数组的所有索引。这创建了一种相当有效的排序方法。那么这又如何适用于手头的问题呢?一种可能的实现可能是......

    static Random rdm = new Random();
    public string[] Shuffle(string[] c)
    {
        var random = rdm;
        for (int i = c.Length; i > 1; i--)
        {
            int iRdm = rdm.Next(i);
            string cTemp = c[iRdm];
            c[iRdm] = c[i - 1];
            c[i - 1] = cTemp;
        }
        return c;
    }

来源:Fisher-Yates Shuffle

上面的代码随机化了字符串数组中值的位置。如果您将Colors和Food数组传递给此函数,则可以通过引用两者的特定索引来为您的Pairs获得唯一的配对。

由于数组是混洗的,因此索引0,1,2等处的两个数组的配对是唯一的。然而问题是要求创建对。然后应该创建一个Pair类,它接收Color和Foods的特定索引的值。即......颜色[3]和食物[3]

 public class Pair
{
     public string One;
     public string Two;
     public Pair(string m1, string m2)
     {
         One = m1;
         Two = m2;
     }
}

由于我们已经对数组和类进行了排序以包含唯一的parings,我们只需创建一个meal数组并用pairs填充它。

如果我们想创建一个新的对,我们会......

Pair temp = new Pair(Colors[0],Foods[0]);

有了这些信息,我们终于可以填充膳食阵列了。

    Pair[] meal = new Pair[Colors.Length - 1];
    for (int i = 0; i < Colors.Length - 1; i++)
    {
        meal[i] = new Pair(Colors[i],Foods[i]);
    } 

这部分代码创建了膳食数组,并按颜色的长度定义了其数量。然后代码循环遍历Color值的总数,同时创建新的pair组合并将其丢弃在用餐中。此方法假设数组的长度相同,可以轻松检查最小的数组。

完整代码

private void Form1_Load(object sender, EventArgs e)
        {
            string[] Colors = new string[] { "red", "orange", "yellow", "green", "blue", "purple" };
            string[] Foods = new string[] { "fruit", "grain", "dairy", "meat", "sweet", "vegetable" };
            Colors = Shuffle(Colors);
            Foods = Shuffle(Foods);
            Pair[] meal = new Pair[Colors.Length - 1];
            for (int i = 0; i < Colors.Length - 1; i++)
            {
                meal[i] = new Pair(Colors[i],Foods[i]);
            }
        }
        static Random rdm = new Random();
        public string[] Shuffle(string[] c)
        {
            var random = rdm;
            for (int i = c.Length; i > 1; i--)
            {
                int iRdm = rdm.Next(i);
                string cTemp = c[iRdm];
                c[iRdm] = c[i - 1];
                c[i - 1] = cTemp;
            }
            return c;
        }
    }
    public class Pair
    {
         public string One;
         public string Two;
         public Pair(string m1, string m2)
         {
             One = m1;
             Two = m2;
         }
     }

- 原始邮寄 -

你可以简单地改组阵列。这将允许相同的方法来填充膳食,但具有不同的结果。 Fisher-Yates shuffle Here

上有一篇文章