.net 在指定的变量范围内查找多个对象,并为该范围内的每个对象变量分配平均值

ctzwtxfj  于 2023-11-20  发布在  .NET
关注(0)|答案(1)|浏览(90)

我有一个对象列表,其中包含变量X、Y和Z。所有对象都是双精度的。现在,我想在一个特定的范围内选择对象,这些对象之间的关系仅基于X。我需要为这个范围内的所有对象分配平均X值,并保持所有成员的Y和Z不变。
我使用“simple”.Sort()对列表进行排序,因为X是第一个。.例如,(仅提及对象中的X变量)。
1013 1500 2200 2240 2251 2290 3100 3500
现在,我需要检查列表中的对象,比如100个。在这个例子中是:2200 2240 2251 2290
我已经尝试使用索引列表作为一个开始

if (DPlace2[i + 1].X - DPlace2[i].X < 100)
   {
    DPlace2[i].X = DPlace2[i].X + ((DPlace2[i + 1].X - DPlace2[i].X) / 2);
    DPlace2[i + 1].X = DPlace2[i + 1].X - ((DPlace2[i + 1].X - DPlace2[i].X) / 2);
   }

字符串
但是在这个范围内可能有任何数量的对象,所以解决方案是垃圾。什么是最好的解决方案?
编辑:为了澄清,这里有一个List代表我的问题的例子。(我最初的问题是一个对象列表,其中需要属性X的值,但这个例子只是显示int以使其更容易阅读。)

List<int> DPlace3 = new List<int>()
{ 
 1175,
 1575,
 2010, //->2015
 2010, //->2015
 2020, //->2015
 3089,
 5050, //->5055
 5060, //->5055
 8100
};


带注解的值表示相对于彼此在100的 * 范围 * 内的对象。也许“范围”在我的问题中是一个草率的表达。对不起!这些行上的注解表示我希望代码将值更改为什么。这就是为什么我对列表进行排序,并开始尝试使用列表中成员的索引,并检查下一个值是否小于100(即范围)从前一个。如果是这样,将索引i的值更改为当前和下一个的平均值。如果范围内总是有2个对象,这很好用,但它可以是5。在上面的例子中,第一个范围涉及3个对象,第二个范围包括2个对象。所以,我在想,一定有一种方法可以很好地编写这个代码。这是一个相当新的东西,所以我不知道SlidingWindow是什么。:)
而且它也是草率的状态“平均值”,因为它是平均值的只有最小/最大值发现在每个值范围.
编辑2:找到了我的问题的解决方案,但它是根据我的特定需求“硬编码”的,肯定不是最优化的(?)对我来说,在值范围内最多可以有8个对象(就目前而言)。所以,我这样做了我的循环:

for (int i = 0; i < DPlace2.Count - 1; i++)
{
    if (((i+7) < DPlace2.Count) && DPlace2[i + 7].X - DPlace2[i].X < 150)
    {
        DPlace2[i].X = DPlace2[i].X + ((DPlace2[i + 7].X - DPlace2[i].X) / 2);
        DPlace2[i + 1].X = DPlace2[i].X;
        DPlace2[i + 2].X = DPlace2[i].X;
        DPlace2[i + 3].X = DPlace2[i].X;
        DPlace2[i + 4].X = DPlace2[i].X;
        DPlace2[i + 5].X = DPlace2[i].X;
        DPlace2[i + 6].X = DPlace2[i].X;
        DPlace2[i + 7].X = DPlace2[i].X;
        i = i + 6;
    }
    if (((i + 6) < DPlace2.Count) && DPlace2[i + 6].X - DPlace2[i].X < 150)
    {
        DPlace2[i].X = DPlace2[i].X + ((DPlace2[i + 6].X - DPlace2[i].X) / 2);
        DPlace2[i + 1].X = DPlace2[i].X;
        DPlace2[i + 2].X = DPlace2[i].X;
        DPlace2[i + 3].X = DPlace2[i].X;
        DPlace2[i + 4].X = DPlace2[i].X;
        DPlace2[i + 5].X = DPlace2[i].X;
        DPlace2[i + 6].X = DPlace2[i].X;
        i = i + 5;
    }
    if (((i + 5) < DPlace2.Count) && DPlace2[i + 5].X - DPlace2[i].X < 150)
    {
        DPlace2[i].X = DPlace2[i].X + ((DPlace2[i + 5].X - DPlace2[i].X) / 2);
        DPlace2[i + 1].X = DPlace2[i].X;
        DPlace2[i + 2].X = DPlace2[i].X;
        DPlace2[i + 3].X = DPlace2[i].X;
        DPlace2[i + 4].X = DPlace2[i].X;
        DPlace2[i + 5].X = DPlace2[i].X;
        i = i + 4;
    }
    if (((i + 4) < DPlace2.Count) && DPlace2[i + 4].X - DPlace2[i].X < 150)
    {
        DPlace2[i].X = DPlace2[i].X + ((DPlace2[i + 4].X - DPlace2[i].X) / 2);
        DPlace2[i + 1].X = DPlace2[i].X;
        DPlace2[i + 2].X = DPlace2[i].X;
        DPlace2[i + 3].X = DPlace2[i].X;
        DPlace2[i + 4].X = DPlace2[i].X;
        i = i + 3;
    }
    if (((i + 3) < DPlace2.Count) && DPlace2[i + 3].X - DPlace2[i].X < 150)
    {
        DPlace2[i].X = DPlace2[i].X + ((DPlace2[i + 3].X - DPlace2[i].X) / 2);
        DPlace2[i + 1].X = DPlace2[i].X;
        DPlace2[i + 2].X = DPlace2[i].X;
        DPlace2[i + 3].X = DPlace2[i].X;
        i = i + 2;
    }
    if (((i + 2) < DPlace2.Count) && DPlace2[i + 2].X - DPlace2[i].X < 150)
    {
        DPlace2[i].X = DPlace2[i].X + ((DPlace2[i + 2].X - DPlace2[i].X) / 2);
        DPlace2[i + 1].X = DPlace2[i].X;
        DPlace2[i + 2].X = DPlace2[i].X;
        i++;
    }
    if (DPlace2[i + 1].X - DPlace2[i].X < 150)
    {
        DPlace2[i].X = DPlace2[i].X + ((DPlace2[i + 1].X - DPlace2[i].X) / 2);
        DPlace2[i + 1].X = DPlace2[i].X;
    }
}


由于列表是排序的,所以min-value总是来自object[i],而max value是来自满足范围内标准的最后一个对象。平均值是从这两个“极值”计算的,因此我的“解决方案”中的所有循环。速度是一个问题,所以我试图通过增加i来停止不必要的循环。即使我认为我现在已经解决了我的问题,无论如何,这都不是一个最优的解决方案。明天可能有9个可能的条目,而不是8个...当然,这个问题一定有一个通用的解决方案。

2vuwiymt

2vuwiymt1#

我想这可能有点复杂,但它似乎是有效的:
因此,在这个解决方案中,我创建了一个执行转换的类。
它的操作假设输入之前已经按X排序,就像它在问题中一样。
该算法基本上是:

  • 浏览数据点列表
  • 如果它是一个序列的第一个数据点,则将其放入一个花名册中并继续
  • 如果名册中已有要素:
  • 如果数据点在X到花名册中第一个项目的+/- 100范围内

然后将其添加到花名册并继续

  • 否则(序列到此结束)
  • 计算花名册中所有X的平均值
  • 对于花名册中的每个元素,在输出中添加一个元素,其中X被计算的平均值替换
  • 清除花名册
  • 将当前数据点添加到花名册
  • 在迭代之后,确保剩余的元素被写入输出
  • 枚举输出
using System;
using System.Linq;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var input = new List<DataPoint>{new DataPoint(1175.0, 1, 1), 
                                        new DataPoint(1575.0, 2, 1), 
                                        new DataPoint(2010.0, 3, 1), 
                                        new DataPoint(2010.0, 4, 1), 
                                        new DataPoint(2020.0, 5, 1), 
                                        new DataPoint(3089.0, 6, 1), 
                                        new DataPoint(5050.0, 7, 1),
                                        new DataPoint(5060.0, 8, 1), 
                                        new DataPoint(8100.0, 9, 1)};
        var smop = new SmoothOperator(100);
        Console.WriteLine($"[{(string.Join(", ", smop.Smooth(input).Select(x => x.ToX())))}]");
    }
}

public record class DataPoint(double X, double Y, double Z)
{
    public string ToX() => $"{X}";
}

public class SmoothOperator
{
    private SmoothOperatorState _state;
    public SmoothOperator(double threshold = 100.0)
    {
        _state = new (threshold);
    }
    
    public IEnumerable<DataPoint> Smooth(IEnumerable<DataPoint> input)
    {
        foreach (var dp in input)
        {
            _state.Process(dp);
        }
        _state.Flush();
        return _state.Outputs;
    }

    private class SmoothOperatorState
    {
        private double _threshold;
        private List<DataPoint> roster = new();
        private List<DataPoint> outputs = new();
        
        public SmoothOperatorState(double threshold) { _threshold = threshold; }
        
        public void Process(DataPoint dp)
        {
            if (roster.Count == 0) // Is this a "first"? 
            {  // Yes => nothing to compare: just add and move on to next
                roster.Add(dp);
                return;
            }
            
            var reference = roster[0].X;
            
            // Is the value within the threshold?
            if ( Math.Abs(dp.X - reference) <= _threshold )
                // Absolute value! Otherwise: 200 - 400 = -200 < 100 is possible ...
            {
                // It is in => add and move on to next
                roster.Add(dp);
                return;
            }
            
            // out of "range => put roster to output
            Flush();
            // and add the current value to the now cleaned out roster.
            roster.Add(dp);
        }

        public void Flush()
        {
            // Get the average of the range
            var median = (int)roster.Select(x => x.X).Distinct().Average();
            // fill outputs with the adjusted datapoints
            outputs.AddRange( roster.Select(x => (x.X == median)?x:new DataPoint(median, x.Y, x.Z)).ToList() );
            // clear out roster for next value
            roster.Clear();
        }

        public IEnumerable<DataPoint> Outputs => outputs.ToList();
    }
}

字符串
在行动:https://dotnetfiddle.net/HMfK53
根据问题中的输入:

Input: [1175, 1575, 2010, 2010, 2020, 3089, 5050, 5060, 8100]
Output:[1175, 1575, 2015, 2015, 2015, 3089, 5055, 5055, 8100]

相关问题