【问题标题】:Algorithm for dynamic combinations动态组合算法
【发布时间】:2018-03-16 17:01:15
【问题描述】:

我的代码有一个名为 INPUTS 的列表,其中包含动态数量的列表,我们称它们为 A、B、C、..N。这些列表包含动态数量的事件

我想为每个事件组合调用一个函数。举例说明:

INPUTS: A(0,1,2), B(0,1), C(0,1,2,3)

我需要为每个组合多次调用我的函数(输入计数是动态的,在本例中是三个参数,但可以或多或少)

function(A[0],B[0],C[0]) 
function(A[0],B[1],C[0]) 
function(A[0],B[0],C[1])
function(A[0],B[1],C[1])
function(A[0],B[0],C[2]) 
function(A[0],B[1],C[2])
function(A[0],B[0],C[3])
function(A[0],B[1],C[3])

function(A[1],B[0],C[0]) 
function(A[1],B[1],C[0]) 
function(A[1],B[0],C[1])
function(A[1],B[1],C[1])
function(A[1],B[0],C[2]) 
function(A[1],B[1],C[2])
function(A[1],B[0],C[3])
function(A[1],B[1],C[3])

function(A[2],B[0],C[0]) 
function(A[2],B[1],C[0]) 
function(A[2],B[0],C[1])
function(A[2],B[1],C[1])
function(A[2],B[0],C[2]) 
function(A[2],B[1],C[2])
function(A[2],B[0],C[3])
function(A[2],B[1],C[3])

这是我目前想到的: 到目前为止,我的方法是建立一个组合列表。元素组合本身就是输入数组 A、B 和 C 的“索引”列表。对于我们的示例:

我的列表 iCOMBINATIONS 包含以下 iCOMBO 列表

(0,0,0) 
(0,1,0) 
(0,0,1)
(0,1,1)
(0,0,2) 
(0,1,2)
(0,0,3)
(0,1,3)

(1,0,0) 
(1,1,0)  
(1,0,1) 
(1,1,1)
(1,0,2) 
(1,1,2)
(1,0,3) 
(1,1,3)

(2,0,0)
(2,1,0)  
(2,0,1) 
(2,1,1)
(2,0,2) 
(2,1,2)
(2,0,3) 
(2,1,3)

然后我会这样做:

foreach( iCOMBO in iCOMBINATIONS)
{
      foreach ( P in INPUTS )
      {
           COMBO.Clear()
           foreach ( i in iCOMBO )
           {
                 COMBO.Add( P[ iCOMBO[i] ] )
           }
           function( COMBO ) --- (instead of passing the events separately)
      }
}

但我需要找到一种方法来为任何给定数量的 INPUTS 及其事件构建列表 iCOMBINATIONS。有什么想法吗?

真的有比这更好的算法吗? 任何可以帮助我的伪代码都会很棒。

C#(或 VB)

谢谢

【问题讨论】:

  • 您可能还想阅读大写约定...msdn.microsoft.com/en-us/library/ms229043.aspx
  • 抛开大写约定,您正在谈论“笛卡尔积”,例如取 X 坐标和 Y 坐标,并查看所有可能的点 (X,Y)。从问题的正确名称开始应该会给您一些进展。请参阅下面的我的 cmets。

标签: c# algorithm combinatorics combinations


【解决方案1】:

您可以使用数组来保存每个列表的索引。示例:

List<List<int>> lists = new List<List<int>> {
  new List<int> { 0,1,2 },
  new List<int> { 0,1 },
  new List<int> { 0,1,2,3 }
};

int[] cnt = new int[lists.Count];
int index;
do {
  Console.WriteLine(String.Join(",", cnt.Select((c,i) => lists[i][c].ToString()).ToArray()));
  index = cnt.Length - 1;
  do {
    cnt[index] = (cnt[index] + 1) % lists[index].Count;
  } while(cnt[index--] == 0 && index != -1);
} while (index != -1 || cnt[0] != 0);

【讨论】:

  • 登录只是为了支持这个解决方案。我一直在寻找的东西。另一个修改是——而不是在列表中列出 0、1、2、3——只需设置列表中的项目数,即 4。这当然不会让我们选择排除某些项目,但是再说一次,这是非常情境化的。 p.s.对于这个解决方案不涉及 LINQ 或 inception-style-loops 的绝对事实,我会给予更多的支持
  • 另外lists[i][c].ToString()也不需要ToString转换。如果您有数百万种组合(就像我一样),这可能会节省一些时间
【解决方案2】:

这是排列问题。你可以看看这个:

http://www.interact-sw.co.uk/iangblog/2004/09/16/permuterate

【讨论】:

  • 不,这不是排列。这是组合。
【解决方案3】:

前段时间我遇到了类似的问题(生成组合),我使用了来自:http://www.merriampark.com/comb.htm 的代码。它是java,但我将它翻译成C#没有任何问题。

【讨论】:

    【解决方案4】:

    将 A,B,C 放入矩阵中! M=[A,B,C]

    recursive_caller(d,params):
        if d == len(M):
            function(params)
            return
    
        for i in M[d]:
            params[d]=i
            recursive_caller(d+1,params)
    

    【讨论】:

      【解决方案5】:

      看起来你真正想要的东西,本身既不是排列,也不是组合。您想查看几个集合的cartesian product(参见here),其中的迭代可能涉及对各个集合的组合进行迭代。

      但是,这与组合问题不同,因为您正在寻找从每个集合中选择 1 个元素的方法。执行此操作的方法的数量是集合的大小。组合问题通常涉及选择 k-从一组 n-许多事物中选择许多事物,其中 k=1 或 n em> 是微不足道的。

      已经讨论了在 C# 中生成迭代器的几种方法here。 (包括 Jon Skeet 的一篇)。

      如果您使用 .NET,您可能还对开发的组合数学模块感兴趣,例如 CodePlex 的 KwCombinatorics

      编辑现在,用 LINQ 来拯救:

      private void cartesian1()
      {
          textAppend("Cartesian 1");
          var setA = new[] { "whole wheat", "white", "rye" };
          var setB = new[] { "cold cut", "veggie", "turkey", "roast beef" };
          var setC = new[] { "everything", "just mayo" };
      
          var query =
              from bread in setA
              from meat in setB
              from toppings in setC
              let sandwich = String.Format("{1} on {0} with {2}",
                  bread, meat, toppings)
              select sandwich;
      
          foreach( string sandwich in query )
          {
              textAppend(sandwich);
          }
      }
      

      【讨论】:

        【解决方案6】:

        @Guffa 答案的修改版本。我绝不是这段代码的创造者。

        List<int> lists = new List<int> { 3, 2, 4 };
        
        int[] cnt = new int[lists.Count];
        int index;
        do
        {
            Console.WriteLine(String.Join(",", cnt));
            index = cnt.Length - 1;
            do
            {
                cnt[index] = (cnt[index] + 1) % lists[index];
            } while (cnt[index--] == 0 && index != -1);
        } while (index != -1 || cnt[0] != 0);
        

        不要使用List&lt;List&lt;int&gt;&gt; - 可能的值 - 使用List&lt;int&gt; 描述集合中元素的数量。输出与原始答案相同。性能更好。

        【讨论】:

          猜你喜欢
          • 2012-01-25
          • 2011-01-31
          • 1970-01-01
          • 2020-05-07
          • 2013-03-10
          相关资源
          最近更新 更多