C# 用c语言构建稀疏阵列的快速算法或技术#

C# 用c语言构建稀疏阵列的快速算法或技术#,c#,arrays,algorithm,sparse-matrix,C#,Arrays,Algorithm,Sparse Matrix,我有一个矩阵构建问题。为了构建矩阵(对于第三方包),我需要通过向第三方对象传递double[]数组来逐行构建矩阵。这是我的问题:我有一个对象列表,表示图形上的路径。每个对象都是一个具有“源”属性(字符串)和“目标”属性(也是字符串)的路径。我需要构建一个一维数组,其中除源属性等于给定名称外,所有元素都是0。给定名称将在路径列表中出现多次。以下是我构建稀疏阵列的函数: static double[] GetNodeSrcRow3(string nodeName) {

我有一个矩阵构建问题。为了构建矩阵(对于第三方包),我需要通过向第三方对象传递double[]数组来逐行构建矩阵。这是我的问题:我有一个对象列表,表示图形上的路径。每个对象都是一个具有“源”属性(字符串)和“目标”属性(也是字符串)的路径。我需要构建一个一维数组,其中除源属性等于给定名称外,所有元素都是0。给定名称将在路径列表中出现多次。以下是我构建稀疏阵列的函数:

    static double[] GetNodeSrcRow3(string nodeName)
    {
        double[] r = new double[cpaths.Count ];
        for (int i = 1; i < cpaths.Count; i++)
        {
            if (cpaths[i].src == nodeName) r[i] = 1;
        }
        return r;
    }
static double[]getNodeScrow3(字符串节点名)
{
double[]r=新的double[cpath.Count];
for(int i=1;i
现在我需要用不同的名字调用这个函数大约20万次。该功能本身需要0.05到0.1秒(使用秒表计时)。你可以想象,如果我们采用0.05秒*200k呼叫=10000秒=2.7小时的最佳情况,这太长了。对象“cpath”包含约20万个对象


有人能想出一种更快的方法来实现这一点吗

我看不到代码的其余部分,但我怀疑大部分时间都花在分配和垃圾收集所有数组上。假设
cpath
的大小不变,您可以重用相同的数组

private static double[] NodeSourceRow == null;
private static List<int> LastSetIndices = new List<int>();

static double[] GetNodeSrcRow3(string nodeName) {
    // create new array *only* on the first call
    NodeSourceRow = NodeSourceRow ?? new double[cpaths.Count];

    // reset all elements to 0
    foreach(int i in LastSetIndices) NodeSourceRow[i] = 0;
    LastSetIndices.Clear();

    // set the 1s
    for (int i = 1; i < cpaths.Count; i++) {
        if (cpaths[i].src == nodeName) {
            NodeSourceRow[i] = 1;
            LastSetIndices.Add(i);
        }
    }

    // tada!!
    return NodeSourceRow;
}
private static double[]节点资源w==null;
私有静态列表LastSetIndexes=新列表();
静态双精度[]GetNodeScrow3(字符串节点名){
//在第一次调用时创建新数组*仅*
NodeSourceRow=NodeSourceRow??新的双精度[cpath.Count];
//将所有元素重置为0
foreach(LastSetIndexes中的int i)节点资源w[i]=0;
lastSetIndexs.Clear();
//设置1
for(int i=1;i

一个潜在的缺点是,如果您需要同时使用所有数组,它们将始终具有相同的内容。但是,如果一次只使用一个线程,则速度应该快得多。

可以使用的方法是使用多线程


如果CPATH是正常列表,则不适合您的情况。您需要一个
src
字典来列出索引。像
字典

然后您可以使用随机访问填充稀疏数组。我还建议您使用稀疏列表实现来提高内存使用效率,而不是使用内存效率低下的
double[]
。一个好的实现是必要的。(大卫·皮普格拉斯撰写)

在生成稀疏列表之前,您应该将
cpath
列表转换为合适的字典,这一步可能需要一点时间(最多几秒钟),但之后您将以极快的速度生成稀疏列表

public static Dictionary<string, List<int>> _dictionary;

public static void CacheIndexes()
{
    _dictionary = cpaths.Select((x, i) => new { index = i, value = x })
                        .GroupBy(x => x.value.src)
                        .ToDictionary(x => x.Key, x => x.Select(a => a.index).ToList());
}

请注意,如果使用
SparseAList
它将占用非常小的空间。例如,如果双数组的长度为
10K
且只有一个索引集,则使用
SparseAList
时,实际上会有
10K
项,但实际上内存中只存储了一个项。使用这些藏品并不难,我建议你试一试

使用
SparseAList

public static SparseAList<double> GetNodeSrcRow3(string nodeName)
{
    SparseAList<double> r = new SparseAList<double>();

    r.InsertSpace(0, cpaths.Count); // allocates zero memory.

    List<int> indexes;
    if(!_dictionary.TryGetValue(nodeName, out indexes)) return r;

    foreach(var index in indexes) r[index] = 1;

    return r;
}
public静态SparseAList getnodescrow3(字符串nodeName)
{
SparseAList r=新SparseAList();
r、 InsertSpace(0,cpath.Count);//分配零内存。
列出索引;
if(!\u dictionary.TryGetValue(nodeName,out index))返回r;
foreach(指数中的var指数)r[指数]=1;
返回r;
}
精彩的答案

请允许我在已经很好的例子中添加一些:

System.Numerics.Tensors.SparseTensor<double> GetNodeSrcRow3(string text)
{

    // A quick NuGet System.Numerics.Tensors Install:
    System.Numerics.Tensors.SparseTensor<double> SparseTensor = new System.Numerics.Tensors.SparseTensor<double>(new int[] { cpaths.Count }, true, 1);

    Parallel.For(1, cpaths.Count, (i, state) =>
    {
        if (cpaths[i].src == nodeName) SparseTensor[i] = 1.0D;
    });

    return SparseTensor;
}
System.Numerics.Tensors.SparseTensor getnodescrow3(字符串文本)
{
//快速NuGet System.Numerics.Tensors安装:
System.Numerics.Tensors.SparseTensor SparseTensor=新的System.Numerics.Tensors.SparseTensor(新的int[]{cpath.Count},真,1);
对于(1,cpath.Count,(i,state)=>
{
如果(cpath[i].src==nodeName)SparseTensor[i]=1.0D;
});
返回斯巴塞传感器;
}
系统。数字被极大地优化,也使用硬件加速。它也是线程安全的。至少从我读到的情况来看


为了提高速度和可扩展性,只需一小段代码就可以实现所有的不同。

什么是CPATH类型?这是一个普通的列表吗?这看起来是一个理想的工作。我想是循环200K*200K扼杀了它。你能一次完成吗?ie创建一个double[][]并对新索引使用nodeName的soem函数-然后只需循环一次。如果第三方库需要一个
double[]
他可能无法使用不同的数据类型。请尝试将
cpath
转换为字典。你正在做一个完整的
cpath行走
,这对性能不好。。。做索引检查肯定会减少花在函数上的时间。我不认为这是需要改进的地方。。。假设您有32个内核,都是空闲的。。。你将把2.7小时缩短到5-6分钟——这还不够。我不知道,这听起来对我来说是一个了不起的进步。我甚至会很高兴,如果这只是加快了10倍。我的答案与并行的混合将更快。OP可以将
getNodeScrow3
作业分配到4个并行运行的内核中。由于OP正在调用此函数
200k
,因此他会在每个核心上调用它
50k
次。不,我需要保留每个数组及其各自独特的内容。谢谢,好主意。接受了!通过使用字典,我把通话时间缩短到了40秒,并且发现一旦我已经叫了一个名字,我就可以大大减少通话次数。很好
public static SparseAList<double> GetNodeSrcRow3(string nodeName)
{
    SparseAList<double> r = new SparseAList<double>();

    r.InsertSpace(0, cpaths.Count); // allocates zero memory.

    List<int> indexes;
    if(!_dictionary.TryGetValue(nodeName, out indexes)) return r;

    foreach(var index in indexes) r[index] = 1;

    return r;
}
System.Numerics.Tensors.SparseTensor<double> GetNodeSrcRow3(string text)
{

    // A quick NuGet System.Numerics.Tensors Install:
    System.Numerics.Tensors.SparseTensor<double> SparseTensor = new System.Numerics.Tensors.SparseTensor<double>(new int[] { cpaths.Count }, true, 1);

    Parallel.For(1, cpaths.Count, (i, state) =>
    {
        if (cpaths[i].src == nodeName) SparseTensor[i] = 1.0D;
    });

    return SparseTensor;
}