C#递归计算值

时间:2018-11-27 02:11:43

标签: c# algorithm recursion

我有下表

enter image description here

我需要计算X,Y和Z列中的值。

规则1:代码仅具有X,Y或Z子级。

让我们从简单的第12行开始,代码“ E2”有一个名为“ Z”的子代,值是3.30,因此单元格[F12](Z列)应该有3.30。我们可以表示为f(12)= 3.30Z,类似地,单元格[E11]应该为2.10,或者f(11)= 2.10Y。对于第6行,cell [D6]应该为1.14,表示为f(6)= 1.14X。

请注意,代码X,Y,Z本身没有孩子。但是,其他孩子可以递归生下大孩子。

规则2:如果代码中有子代码,则该值将为其自身值乘以子代码值。

例如第10行,它有一个子级'E2',因此该值将是其子项(E2)值的3.10倍,即f(10)= 3.10 * f(12)= 3.10 * 3.30Z = 10.23Z,单元格[F10]应该是10.23

规则3,如果一个代码中有多个子代,则该值将按照规则2按类型对所有子代分组求和。

例如第5行C1有一个孩子D,D有三个孩子(X,E1,E2),这3个孩子值需要加在​​一起。 f(5)= 1.70 *(f(8)+ f(9)+ f(10))。当然,有些子项具有X值,有些子项具有Y值,有些子项具有Z值,所以在将它们求和时,需要按值类型将它们分组,然后填写相应的列Cell [D5],Cell [E5] ,单元格[F5]

希望如此。

这是原始更复杂问题的简化版本,其中包含数千行和其他一些计算。但是不是一个巨大的表,因此性能或速度不是优先考虑的问题。

更多的是算法问题,但我更喜欢用C#实现

2 个答案:

答案 0 :(得分:3)

您所描述的是一个树状结构,您可以轻松地构建它,然后进行递归遍历。您已经知道父母/孩子的关系;您只需要创建一个模型即可在代码中表示它们。

从一个Element类开始,如下所示:

class Element
{
    string Code;
    Element Parent;  // null if no parent
    double Value;
    double CalculatedValue;
    List<string> Children;
}

并创建一个这些字典,以代码为键:

Dictionary<string, Element> Elements;

遍历列表,添加为每个唯一元素创建一个Element并将其添加到字典中:

(注意:代码示例使用伪C#。我不想在这里陷入语法困境。)

for each row
{
    Element e;

    // if the element doesn't exist in the dictionary, create and add it
    if (!Elements.TryGetValue(code, out e))
    {
        e = new Element(code, value);
        Elements.Add(code, e);
    }
    if (child != null)
    {
        e.Children.Add(child);
    }
}

您现在有了一个包含子关系的元素字典。您需要创建父级关系。最简单的方法是扫描字典:

for each e in Elements
    for each child in e.Children
        if (child != "X" or "Y" or "Z")
            Elements[child].Parent = e;

现在您拥有的是森林:一棵或多棵无根树木。您可以递归扫描它以计算您的值:

double totalValue = 0;
for each e in Elements
    if (e.Parent == null)
    {
        totalValue += calculateElementValue(e);
    }
}
// at this point, totalValue should contain the total value

如果我正确理解了您的规则,则此方法应计算单个元素的值。我假设一个元素只有X,Y或Z中的一个。这是对元素树的简单的深度优先遍历。

double CalculateElementValue(Element e)
{
    double eValue = 0;
    double childrenValue = 0;
    for each child in e.Children
    {
        switch (child)
        {
            case "X": eValue = e.Value * xValue; break;
            case "Y": eValue = e.Value * yValue; break;
            case "Z": eValue = e.Value * zValue; break;
            else
            {
                childrenValue += CalculateElementValue(Elements[child]);
            }
        }
    }
    return eValue * childrenValue;
}

您可以在构建元素的初始循环中包括父代分配,但是这会使事情变得有些复杂。但是除非表中有很多行,否则您可能不会注意到速度的差异。

答案 1 :(得分:1)

以下是一些示例数据结构,可用于表示数据并递归计算值。

请注意,此示例使用了间接递归。即Node.Evaluate()不会直接调用自身,而是会调用WeightedChild.Evaluate(),后者又会返回到Node.Evaluate()

public class Node
{
    public Node(params WeightedChild[] weightedChildren)
    {
        this.WeightedChildren = weightedChildren ?? Enumerable.Empty<WeightedChild>();
    }

    IEnumerable<WeightedChild> WeightedChildren { get; }

    public double Evaluate()
    {
        return this.WeightedChildren.Any()
            ? this.WeightedChildren.Select(child => child.Evaluate()).Sum()
            : 1;
    }
}

public class WeightedChild
{
    public WeightedChild(double weight, Node child)
    {
        this.Weight = weight;
        this.Child = child;
    }

    public double Weight { get; }
    Node Child { get; }

    public double Evaluate()
    {
        return this.Child.Evaluate() * this.Weight;
    }
}

然后您可以使用这些类来构建数据集:

var X = new Node();
var Y = new Node();
var Z = new Node();
var E2 = new Node(new WeightedChild(3.3, Z));
var E1 = new Node(new WeightedChild(2.1, Y));
var D = new Node(
    new WeightedChild(0.7, X),
    new WeightedChild(1.8, E1),
    new WeightedChild(3.1, E2));
var C2 = new Node(new WeightedChild(0.9, X));
var C1 = new Node(
    new WeightedChild(1.14, X),
    new WeightedChild(1.7, D));
var B = new Node(
    new WeightedChild(1.3, C1),
    new WeightedChild(1.5, C2));
var A = new Node(new WeightedChild(1.1, C1));

然后您可以评估每个节点:

Console.WriteLine($"f(E2) = {E2.Evaluate()}");
Console.WriteLine($"f(D) = {D.Evaluate()}");
Console.WriteLine($"f(C1) = {C1.Evaluate()}");

但是请注意,在规则2的示例中,您是从第10行中删除单个“ D”,此答案中定义的集合捕获了D有多个子代的事实,因此将给出不同的答案。您可以通过将D重新定义为D1D2D3来解决此问题,然后将它们全部添加为C1等的子代。

相关问题