C# 列表的除和并集的替代方案

C# 列表的除和并集的替代方案,c#,performance,list,union,except,C#,Performance,List,Union,Except,对于这篇冗长的帖子,我提前表示歉意,感谢所有愿意花时间来结束文章并给我反馈的人 我有关于列表和数组操作的性能相关问题 我已经编写了一个软件来对从传感器阵列收集的数据执行一些操作。为了让它运行得更快,我目前正在尝试编写一些优化 收集的数据是一个N×M的双精度数组(实际上是作为扩展列表的类实现的)。对于i的任何值,我们总是有this.Count()==N和this[i].Count()==M。基本上,它是一个矩形阵列 此数组中的每个数据点都与X-X-Y地图上的某些点相关。基本上,假设这是一幅我用数据

对于这篇冗长的帖子,我提前表示歉意,感谢所有愿意花时间来结束文章并给我反馈的人

我有关于列表和数组操作的性能相关问题

我已经编写了一个软件来对从传感器阵列收集的数据执行一些操作。为了让它运行得更快,我目前正在尝试编写一些优化

收集的数据是一个N×M的双精度数组(实际上是作为扩展
列表的类实现的)。对于i的任何值,我们总是有
this.Count()==N
this[i].Count()==M
。基本上,它是一个矩形阵列

此数组中的每个数据点都与X-X-Y地图上的某些点相关。基本上,假设这是一幅我用数据制作的图像,以快速清晰的方式表示它。因此,对于每个数据点,都有一个与其相关的映射点列表。这一事实由
列表[,]pointsLocal
表示。我还保存了一个静态的
列表[,]
,在那里我存储了相同的信息:这样我就可以在精化循环中随意修改
pointsLocal
,并在下次调用这些方法时有一个新的副本。相同的传感器总是与相同的点相关,这就是为什么我有那个本地阵列。有些点(实际上大多数点)与多个传感器相关,因此在许多列表中

在我的代码的其他部分中,我能够正确地识别阵列的某些传感器有一些问题,然后数据包含错误。这在
私有列表faultyData
中表示。如果传感器输出有故障,那么我必须假设所有与其相关的点都可能出现故障,因此我不关心这些映射点的进一步分析结果

我的代码的计算部分为每个地图点聚合来自阵列中所有传感器的数据。我想做的是预先确定一个地图点的子集,我不必对其进行任何分析

PointsEqualityComparer
int[]
的自定义比较运算符,我使用它是因为地图点由它们的二维坐标标识

public class Sinogram : List<List<double>>
{
    //various enums
   private List<List<bool>> faultyData; //faultyData[i][j] is true if there is an error in the data
    //constructors
    //some methods
    public void dataAnalysis()
    {
        List<int[]>[,] pointsLocal = new List<int[]>[this.Count(), this[0].Count()];
        List<int[]> faultyPoints = new List<int[]>();
        //Fill pointsLocal with the correlated points from the static array
        PointsEqualityComparer myComparer = new PointsEqualityComparer();
        //Point selection parts (see later for the two different implementations)
        //Actual analysis parts (not here because it is not relevant to my question, but it works)
    }
}
现在来看棘手的部分。对于代码部分,我有两种不同的实现,我实际上选择了哪些映射点是有趣的。我所说的“有趣”是指我必须聚集传感器数据的地图点。我通过实际识别可能出现错误的点并将其从列表中删除来选择它们

在我的第一个实现中,我循环遍历所有映射点列表。如果相应的传感器出现故障,我会将这些点添加到故障点列表中(避免重复)。一旦我遍历了所有点并生成了故障点的完整列表,我就会通过删除它们来更新
allPairsLocal
。faultyPoints列表可能会变得相当大,特别是在许多传感器报告错误的情况下(如果所有传感器报告错误,并且我正在尝试创建一个1920*1080的地图以绘制为高清图像,则最大理论大小超过2000000个元素)


for(int i=0;i如果我理解正确,一旦您填充
pointsLocal
阵列,我们为每个传感器
(i,j)

  • 此[i][j]
    =来自传感器的数据
    (i,j)
  • 点定位[i,j]
    =传感器的地图点列表
    (i,j)
  • faultyData[i][j]
    =如果传感器
    (i,j)
    的数据不正确,则为true,否则为false
考虑“反转”数据,以便给定一个映射点
(x,y)
,您可以高效地

  • 查明该点是否有故障(即地图点的任何传感器报告数据有故障)
  • 获取报告与地图点相关数据的传感器列表
为此,我们可以创建一个使用您已经编写的比较器的字典。每个键都是表示映射点的
(x,y)
对(即
int[2]
),返回的值(如果有的话)是贡献于该点的已知传感器的列表。返回值
null
表示映射点为空“受感染”的传感器有故障,应该忽略。如果字典中根本不存在给定的传感器对,则表示没有传感器导致该点

var mapPoints = new Dictionary<int[], List<int[]>)(PointsEqualityComparer);

for (int i = 0; i <this.Count; i++)
{
    for (int j = 0; j < this[i].Count; j++)
    {
        foreach (var point in pointsLocal[i,j]) 
        {
            if (faultyData[i][j])
            {
                // infected point
                mapPoints[point] = null;  
            }
            else
            {
                // Add the current sensor's indices (i,j) to the list of 
                // known sensors for the current map point

                List<int[]> sensors = null;
                if (!mapPoints.TryGetValue(point, out sensors)) 
                {
                    sensors = new List<int[]>();
                    mapPoints[point] = sensors;
                }

                // null here means that we previously determined that the
                // current map point is infected 
                if (sensors != null) 
                {
                    // Add sensor to list for this map point
                    sensors.Add(new int[] { i, j });
                }
            }
        }
    } 
}
请注意,我没有更改
allPairsLocal
,因为分析步骤似乎不需要这样做。但是,如果您确实需要从中删除错误的映射点,您也可以高效地执行此操作:

for (int i = 0; i <this.Count; i++)
{
    for (int j = 0; j < this[i].Count; j++)
    {
        var points = allPairsLocal[i][j];
        var cleanedUp = new List<int[]>();
        foreach (var point in points) 
        {
            // Important: do NOT use 'faultyPoints' here. It will kill performance
            if (mapPoints[point] != null)
            {
               cleanedUp.Add(point); 
            }
        }
        allPairsLocal[i][j] = cleanedUp;   
    }
}

for(inti=0;i是的,你是对的。这是因为联合和除运算的复杂性。
您有N-by-M传感器表(您在上面将其命名为地图点列表)。每个传感器影响一个点阵列(您将其命名为
allPairsLocal[i,j]
)。每个点阵列都是全局预定点阵列的子集(X-by-Y地图上的
点)。
如果我是对的,那么:

  • X-by-Y地图上的点-这是一个点的全局数组。更重要的是,因为你可以比较点,所以你可以对它们进行排序,并保持该数组的排序(我的意思是可能没有实际排序,但具有良好的读取操作复杂性)。使用
    字典
    查找关键点的坐标、值-顺序索引(在插入所有点后设置它)
  • 现在我们有了一组传感器(让我们将步骤1中的
    字典
    命名为点)。我们需要构造两个映射-一个
    传感器两点
    (命名为s2p)和
    点传感器
    (命名为p2s)。您将
    所有pairsLocal
    命名为传感器两点,看起来像
    列表[]
    ,即每个传感器的点坐标列表。但我们需要保持点坐标索引列表
    for (int i = 0; i <this.Count; i++)
    {
        for (int j = 0; j < this[i].Count; j++)
        {
            if (faultyData[i][j])
            {
                faultyPoints = allPairsLocal[i, j]. ToList();
                for (int x = 0; x < this.Count; x++)
                {
                    for (int y = 0; y < this[x].Count; y++)
                    {
                        allPairsLocal[x, y] = allPairsLocal[x, y].Except(faultyPoints, myComparer).ToList();
                    }
                }
            }
        }
    }
    
    var mapPoints = new Dictionary<int[], List<int[]>)(PointsEqualityComparer);
    
    for (int i = 0; i <this.Count; i++)
    {
        for (int j = 0; j < this[i].Count; j++)
        {
            foreach (var point in pointsLocal[i,j]) 
            {
                if (faultyData[i][j])
                {
                    // infected point
                    mapPoints[point] = null;  
                }
                else
                {
                    // Add the current sensor's indices (i,j) to the list of 
                    // known sensors for the current map point
    
                    List<int[]> sensors = null;
                    if (!mapPoints.TryGetValue(point, out sensors)) 
                    {
                        sensors = new List<int[]>();
                        mapPoints[point] = sensors;
                    }
    
                    // null here means that we previously determined that the
                    // current map point is infected 
                    if (sensors != null) 
                    {
                        // Add sensor to list for this map point
                        sensors.Add(new int[] { i, j });
                    }
                }
            }
        } 
    }
    
    var faultyPoints = new List<int[]>();  // not sure you really need this? 
    var goodPoints = new List<int[]>();
    foreach (var point in mapPoints.Keys)
    {
        var sensors = mapPoints[point];
        if (sensors == null)
             faultyPoints.Add(point);
        else
             goodPoints.Add(point);
    }
    
    foreach (var point in goodPoints) 
    {
        var sensors = mapPoints[point]; 
        // for current point, aggregate data for each sensor listed in "sensors"
    }
    
    for (int i = 0; i <this.Count; i++)
    {
        for (int j = 0; j < this[i].Count; j++)
        {
            var points = allPairsLocal[i][j];
            var cleanedUp = new List<int[]>();
            foreach (var point in points) 
            {
                // Important: do NOT use 'faultyPoints' here. It will kill performance
                if (mapPoints[point] != null)
                {
                   cleanedUp.Add(point); 
                }
            }
            allPairsLocal[i][j] = cleanedUp;   
        }
    }
    
    // straight and inverted mappings
    var s2p = new List<int>[N*M];
    var p2s = new List<List<int>>(point.Count);
    //and initialize p2s inner lists
    for (int i = 0; i < p2s.Count; i++)
        p2s[i] = new List<int>();
    
    for (int i = 0; i < N * M; i++)
    {
        s2p[i] = new List<int>(allPairsLocal[i/M][i%M].Count);
    
        //convert list of points coordinates to list of it's indices
        // and construct inverted mapping
        foreach(int[] p in allPairsLocal[i/M][i%M])
        {
            // points[p] - index of point p in Dictionary if you remember
            s2p[i].Add(points[p]);
            p2s[points[p]].Add(i);
        }            
    }
    
    //I don't know which set you need as a result - valid points or sensors so I do both
    
    // false - correct, true - error. Initialized with false
    BitArray sensorsMask = new BitArray(sensors.Count);
    BitArray pointsMask = new BitArray(points.Count);
    
    for (int i = 0; i < N * M; i++)
    {
        if (faultyData[i / M][i % M])
            sensorsMask[i] = true; // means error in sensor
    
        foreach(int p in s2p[i])
            pointsMask[p] = true;
    }
    
    // so you can get only valid sensors
    var validSensors = new List<int>();
    for (int i = 0; i < N * M; i++)
        if (!sensorsMask[i])
            validSensors.Add(i);
    
    // or only valid points
    var validPoints = new List<int[]>();
    foreach (var pair in points)
        if (!pointsMask[pair.Value])
            validPoints.Add(points.Key);