来自不同群体的数字组合

时间:2018-06-01 06:56:23

标签: c# math

我有5组,每组有4个不同的数字;我想制作所有可能的组合(每个都有4个数字),这样就可以从一组中选择2个数字,而从其余组中选择其余数字(每个数字一个)。我已经在很多地方进行了搜索,并成功地编写了代码来从1组中选择组合,但很难找到上述方案的解决方案。 任何人都可以给出一些指示吗?谢谢你的帮助!

输入: -

Group 1: 1,2,3,4
Group 2: 7,8,9,10
Group 3: 15,16,17,18
Group 4: 22,23,24,25
Group 5: 27,28,29,30

预期产出: -

1,2,7,15 (2 numbers from group 1 and 1 each from group 2 and 3)
7,8,1,22 (2 numbers from group 2 and 1 each from group 1 and 4)

依旧......

以下是我的代码: -

private static bool NextCombination(IList<int> num, int n, int k)
      {
         bool finished;

         var changed = finished = false;

         if (k <= 0) return false;

         for (var i = k - 1; !finished && !changed; i--)
         {
            if (num[i] < n - 1 - (k - 1) + i)
            {
               num[i]++;

               if (i < k - 1)
                  for (var j = i + 1; j < k; j++)
                     num[j] = num[j - 1] + 1;
               changed = true;
            }
            finished = i == 0;
         }


     return changed;
  }

  private static IEnumerable Combinations<T>(IEnumerable<T> elements, int k)
  {
     var elem = elements.ToArray();
     var size = elem.Length;

     if (k > size) yield break;

     var numbers = new int[k];

     for (var i = 0; i < k; i++)
        numbers[i] = i;

     do
     {
        yield return numbers.Select(n => elem[n]);
     } while (NextCombination(numbers, size, k));
  }


  private static void Main()
  {
     const int k = 3;
     var n = new[] {"1", "2", "3", "4", "5"};

     Console.Write("n: " );
     foreach (var item in n)
     {
        Console.Write("{0} ", item);
     }
     Console.WriteLine();
     Console.WriteLine("k: {0}", k);
     Console.WriteLine();

     foreach (IEnumerable<string> i in Combinations(n, k))
        Console.WriteLine(string.Join(" ", i));
  }
}

1 个答案:

答案 0 :(得分:2)

以下是一段代码,可以执行以下操作:

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    class SelectedGroups<T>
    {
        public readonly IList<T> Choose2; // group to Chose 2 elements from
        public readonly IList<T> Choose1_1; // first group to Chose 1 element from
        public readonly IList<T> Choose1_2; // second group to Chose 1 element from

        public SelectedGroups(IList<T> choose2, IList<T> choose11, IList<T> choose12)
        {
            Choose2 = choose2;
            Choose1_1 = choose11;
            Choose1_2 = choose12;
        }
    }

    static IEnumerable<SelectedGroups<T>> ChooseGroups211<T>(IList<IList<T>> groups)
    {
        for (var i = 0; i < groups.Count; i++)
        {
            var outer = groups[i];
            for (var j = 0; j < groups.Count - 1; j++)
            {
                if (i == j)
                    continue;
                var first = groups[j];
                // start from j+1 so  k > j so groups[k] and groups[j] cover all the groups pairs excactly once
                for (var k = j + 1; k < groups.Count; k++) 
                {
                    if (i == k)
                        continue;
                    yield return new SelectedGroups<T>(outer, first, groups[k]); ;
                }
            }
        }
    }

    public class SelectionResult<T>
    {
        public readonly T Value11; // first value from the group #1
        public readonly T Value12; // second value from the group #1
        public readonly T Value3; // value from the group #2
        public readonly T Value4; // value from the group #3


        public SelectionResult(T value11, T value12, T value3, T value4)
        {
            Value11 = value11;
            Value12 = value12;
            Value3 = value3;
            Value4 = value4;
        }

        public override string ToString()
        {
            return string.Format("{0} {1} {2} {3}", Value11, Value12, Value3, Value4);
        }
    }

    static IEnumerable<SelectionResult<T>> Select211FromGroups<T>(SelectedGroups<T> groups)
    {
        for (var i = 0; i < groups.Choose2.Count - 1; i++)
        {
            var value11 = groups.Choose2[i];
            // start from i+1 so  j > i so groups.Choose2[i] and groups.Choose2[j] cover all the pairs excactly once
            for (var j = i + 1; j < groups.Choose2.Count; j++)
            {
                var value12 = groups.Choose2[j];
                foreach (var value3 in groups.Choose1_1)
                {
                    foreach (var value4 in groups.Choose1_2)
                    {
                        yield return new SelectionResult<T>(value11, value12, value3, value4);
                    }
                }

            }
        }
    }

    public static IEnumerable<SelectionResult<T>> Select211<T>(IList<IList<T>> groups)
    {
        return ChooseGroups211(groups).SelectMany(g => Select211FromGroups(g));
    }

    public static void Main(string[] args)
    {
        //PrintHex(4);
        List<int> g1 = new List<int>() { 1, 2, 3, 4 };
        List<int> g2 = new List<int>() { 7, 8, 9, 10 };
        List<int> g3 = new List<int>() { 15, 16, 17, 18 };
        List<int> g4 = new List<int>() { 22, 23, 24, 25 };
        List<int> g5 = new List<int>() { 27, 28, 29, 30 };

        var allGroups = new List<IList<int>>() { g1, g2, g3, g4, g5 };

        foreach (var selectionResult in Select211(allGroups))
        {
            Console.WriteLine(selectionResult);
        }
    }
}

请参阅online demo

代码背后的想法如下:

  1. 首先从所有组3中选择以从中选择元素。即生成所有此类IEnumerable的{​​{1}}。这是通过方法SelectedGroups完成的。这里重点是ChooseGroups211Choose1_1是完全对称的,因此您只想选择两对Choose1_2(Gi, Gj)

  2. 从每个固定(Gj, Gi)生成4个元素(SelectedGroups)的所有组合。这是通过方法SelectionResult<T>完成的。同样Select211FromGroupsValue11是对称的,因此您只想选择两对中的一对。

  3. 使用SelectMany将这些构建基块加入到您想要的方法中,Value12

  4. 注意:这段代码非常简单,但同时它对于这个特定的任务来说几乎是硬编码的,即选择恰好4个元素为2 + 1 + 1.这允许我使用命名类型而不是Select211的结果和中间结果,我通常更喜欢。如果要求不同,我可能会使用通用的IEnumerable<T>或数组IEnumerable以及一些更高级的技巧,例如Computing a Cartesian product with LINQ