6

我敢肯定这已经被问了一百万次了,但是当我搜索所有的例子时,都不太合适,所以我想我还是应该问一下。

我有两个数组,每个数组总是包含 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;

我的“膳食”清单中没有任何元素可以重复。所以只有一个“红色”项目,一个“肉”项目,等等。

我可以根据前两个数组轻松创建对,但我对如何最好地将它们分组为独特的组合持空白。

4

3 回答 3

5

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

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

  • 随机产生一个排列
  • 用未排列的第一个数组压缩排列的第二个数组
  • 从查询中生成一个数组

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

从获取这个库开始:

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

好的,我们需要对六个项目进行所有排列:

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 中生成排列的博客文章;我可能会以此为例!)

于 2013-02-27T01:22:38.577 回答
4

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

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


首先,产生第二个数组的排列。您可以使用 Fischer-Yates-Knuth shuffle 就地完成;StackOverflow 上有很多关于如何做到这一点的例子。或者,您可以通过使用随机键进行排序来使用 LINQ 生成排列。

即使项目数量很大,前一种技术也很快,但是会改变现有数组。第二种技术速度较慢,尤其是在项目数量非常大的情况下,事实并非如此。

人们在使用第二种技术时最常犯的错误是根据指南进行排序。Guid 保证是唯一的,不保证是随机的。

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

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();

十分简单。

于 2013-02-27T00:53:03.827 回答
1

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

最初阅读这个问题时,我想起了整理一副纸牌。这两个数组非常类似于西装数组和面值数组。由于解决洗牌的一种方法是将数组随机化,然后选择一张将两者结合的牌,因此您可以在此处应用相同的逻辑。

排序作为一种可能的解决方案

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 类,该类在 Colors 和 Foods 的特定索引处接收一个值。即...颜色[3] 和食物[3]

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

由于我们已经对数组和一个类进行了排序以包含唯一的配对,因此我们只需创建膳食数组并使用 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]);
    } 

这部分代码创建膳食数组并通过颜色的长度定义其索引数。然后代码循环遍历颜色值的总数,同时创建新的配对组合并将它们放入餐中。此方法假设数组的长度相同,可以轻松检查最小的数组。

完整代码

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的帖子

于 2013-02-27T00:31:29.857 回答