【问题标题】:Cartesian Product of an arbitrary number of objects [duplicate]任意数量对象的笛卡尔积[重复]
【发布时间】:2018-04-10 16:50:47
【问题描述】:

我正在寻找 C# 中任意数量对象的笛卡尔积。我的情况有点不寻常——我的输入不是基本类型列表,而是具有基本类型列表属性的对象。

我的输入输出对象如下:

public class Input
{
    public string Label;
    public List<int> Ids;
}

public class Result
{
    public string Label;
    public int Id;
}

一些示例输入数据:

var inputs = new List<Input>
{
    new Input { Label = "List1", Ids = new List<int>{ 1, 2 } },
    new Input { Label = "List2", Ids = new List<int>{ 2, 3 } },
    new Input { Label = "List3", Ids = new List<int>{ 4 } }
};

还有我预期的输出对象:

var expectedResult = new List<List<Result>>
{
    new List<Result>
    {
        new Result{Label = "List1", Id = 1},
        new Result{Label = "List2", Id = 2},
        new Result{Label = "List3", Id = 4}
    },
    new List<Result>
    {
        new Result{Label = "List1", Id = 1},
        new Result{Label = "List2", Id = 3},
        new Result{Label = "List3", Id = 4}
    },
    new List<Result>
    {
        new Result{Label = "List1", Id = 2},
        new Result{Label = "List2", Id = 2},
        new Result{Label = "List3", Id = 4}
    },
    new List<Result>
    {
        new Result{Label = "List1", Id = 2},
        new Result{Label = "List2", Id = 3},
        new Result{Label = "List3", Id = 4}
    }
};

如果我提前知道“输入”中的项目数,我可以这样做:

var knownInputResult = 
    from id1 in inputs[0].Ids
    from id2 in inputs[1].Ids
    from id3 in inputs[2].Ids
    select
    new List<Result>
    {
        new Result { Id = id1, Label = inputs[0].Label },
        new Result { Id = id2, Label = inputs[1].Label },
        new Result { Id = id3, Label = inputs[2].Label },
    };

我正在努力使其适应任意数量的输入 - 有没有可能的方法来做到这一点?

【问题讨论】:

  • 感谢您查看此 Erno。我已经查看了各种笛卡尔积答案——但是它们都处理基本类型的输入(例如,List&lt;int&gt;)。我希望在我的输入列表中使用一个属性作为笛卡尔积的比较,这使它有点棘手。重复链接和评论中的链接都没有解决这部分问题,请问您可以重新打开吗?

标签: c#


【解决方案1】:

我认为在 cmets 中链接了这个重复的问题,但是由于它已重新打开,并且您很难根据您的情况调整该问题,因此方法如下。

Eric Lippert 的第一个抓取功能按原样从重复的问题中提取(那里解释了它的工作原理):

public static class Extensions {
    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
    {
        IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
        return sequences.Aggregate(
            emptyProduct,
            (accumulator, sequence) =>
                from accseq in accumulator
                from item in sequence
                select accseq.Concat(new[] { item })
        );
    }
}

然后展平您的输入。基本上只是为每个id附加相应的标签:

var flatten = inputs.Select(c => c.Ids.Select(r => new Result {Label = c.Label, Id = r}));

然后运行笛卡尔积并完成:

// your expected result
var result = flatten.CartesianProduct().Select(r => r.ToList()).ToList();            

【讨论】:

    【解决方案2】:

    我并不为我花了这么多时间搞砸这个而感到自豪,但它确实有效。 它基本上是黑魔法,你一有机会我就会把它换掉。

    public static List<List<Result>> Permutate(IEnumerable<Input> inputs)
    {
        List<List<Result>> results = new List<List<Result>>();
    
        var size = inputs.Select(inp => factorial_WhileLoop(inp.Ids.Count)).Aggregate((item, carry) => item + carry) - 1;
    
        for (int i = 0; i < size; i++) results.Add(new List<Result>());
    
        foreach (var input in inputs)
        {
            for (int j = 0; j < input.Ids.Count; j++)
            {
                for (int i = 0; i < (size / input.Ids.Count); i++)
                {
                    var x = new Result() { Label = input.Label, Id = input.Ids[j] };
    
                    results[(input.Ids.Count * i) + j].Add(x);
                }
            }
        }
    
        return results;
    }
    
    public static int factorial_WhileLoop(int number)
    {
        var result = 1;
        while (number != 1)
        {
            result = result * number;
            number = number - 1;
        }
        return result;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-17
      • 2020-02-03
      • 1970-01-01
      • 2016-11-22
      • 2012-02-24
      • 2011-09-18
      相关资源
      最近更新 更多