【问题标题】:C# recursively calculate valueC#递归计算值
【发布时间】:2018-11-27 02:11:42
【问题描述】:

我有下表

我需要计算出 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# 实现

【问题讨论】:

  • 这是一个非常广泛的问题,我们很难回答。如果您尝试过某事并遇到特定问题,也许我们可以提供帮助?
  • 完全不清楚你卡在哪一部分。请显示未按预期工作的代码(SO 不是代码编写服务)。
  • Peeyush。数据是正确的。代码代表化学成分。例如,第 6 行表示成分 C1 中有 1.14% 的成分 X。原来的化学代码很复杂,我这里换成伪代码来解决问题。
  • Rufus,我不知道从哪里开始这段代码。这绝对不是 for each 循环,必须是某种递归,但我不知道该怎么做。我将继续努力,希望能找到解决方案并将其发布在这里。同时,任何人给我一些建议将不胜感激。

标签: c# algorithm recursion


【解决方案1】:

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

从如下所示的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;
}

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

【讨论】:

    【解决方案2】:

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

    请注意,此示例使用间接递归。 IE。 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 等的子代。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-21
      • 1970-01-01
      • 2015-07-28
      • 1970-01-01
      • 1970-01-01
      • 2019-09-03
      • 2015-01-08
      • 2021-11-01
      相关资源
      最近更新 更多