并行化传递减少

时间:2015-11-28 12:05:44

标签: c# algorithm parallel-processing

我有一个Dictionary<int, List<int>>,其中Key表示集合的元素(或面向图中的顶点),List是一组与Key相关的其他元素(所以有从键到值的定向边缘)。字典已针对创建Hasse图进行了优化,因此值始终小于Key。

我还有一个简单的顺序算法,删除所有传递边缘(例如,我有关系1-> 2,2-> 3和1-> 3.我可以删除边缘1-> 3,因为我有1到3之间的路径2)。

for(int i = 1; i < dictionary.Count; i++)
{
    for(int j = 0; j < i; j++)
    {
        if(dictionary[i].Contains(j))
                dictionary[i].RemoveAll(r => dictionary[j].Contains(r));
    }
}

是否可以并行化算法?我可以做内部循环的Parallel.For。但是,建议不要这样做(https://msdn.microsoft.com/en-us/library/dd997392(v=vs.110).aspx#Anchor_2),结果速度不会显着增加(+可能存在锁定问题)。我可以并行化外循环吗?

1 个答案:

答案 0 :(得分:0)

有一种简单的方法可以解决并行化问题,分离数据。从原始数据结构读取并写入新的。这样你就可以并行运行它,甚至不需要锁定。

但可能并行化并不是必需的,数据结构效率不高。你使用数组就足够了(因为我理解你有顶点0..result.Count-1的代码)。 List<int>用于查找。 List.Contains非常低效。 HashSet会更好。或者,对于更密集的图表,BitArray。因此,而不是Dictionary<int, List<int>>您可以使用BitArray[]

我重写了算法并进行了一些优化。它不会生成图形的简单副本并删除边缘,它只是从右边缘构造新图形。它使用BitArray[]作为输入图,List<int>[]作为最终图,因为后者更稀疏。

int sizeOfGraph = 1000;

//create vertices of a graph
BitArray[] inputGraph = new BitArray[sizeOfGraph];
for (int i = 0; i < inputGraph.Length; ++i)
{
    inputGraph[i] = new BitArray(i);
}

//fill random edges
Random rand = new Random(10);
for (int i = 1; i < inputGraph.Length; ++i)
{
    BitArray vertex_i = inputGraph[i];
    for(int j = 0; j < vertex_i.Count; ++j)
    {
        if(rand.Next(0, 100) < 50) //50% fill ratio
        {
            vertex_i[j] = true;
        }
    }
}

//create transitive closure
for (int i = 0; i < sizeOfGraph; ++i)
{
    BitArray vertex_i = inputGraph[i];
    for (int j = 0; j < i; ++j)
    {
        if (vertex_i[j]) { continue; }
        for (int r = j + 1; r < i; ++r)
        {
            if (vertex_i[r] && inputGraph[r][j])
            {
                vertex_i[j] = true;
                break;
            }
        }
    }
}

//create transitive reduction
List<int>[] reducedGraph = new List<int>[sizeOfGraph];
Parallel.ForEach(inputGraph, (vertex_i, state, ii) =>
{
    {
        int i = (int)ii;
        List<int> reducedVertex = reducedGraph[i] = new List<int>();

        for (int j = i - 1; j >= 0; --j)
        {
            if (vertex_i[j])
            {
                bool ok = true;
                for (int x = 0; x < reducedVertex.Count; ++x)
                {
                    if (inputGraph[reducedVertex[x]][j])
                    {
                        ok = false;
                        break;
                    }
                }
                if (ok)
                {
                    reducedVertex.Add(j);
                }
            }
        }
    }
});

MessageBox.Show("Finished, reduced graph has "
    + reducedGraph.Sum(s => s.Count()) + " edges.");

修改

我写了这个: 代码有一些问题。随着方向i现在,您可以删除您需要的边缘,结果将是不正确的。结果证明是错误的。我这样想,让我们有一个图表

1->0
2->1, 2->0
3->2, 3->1, 3->0

顶点2被顶点1缩小,所以我们有

1->0
2->1
3->2, 3->1, 3->0

现在顶点3被顶点2缩小

1->0
2->1
3->2, 3->0

我们遇到了一个问题,因为我们无法减少由于3->0减少而留在这里的2->0。但这是我的错,这绝不会发生。内循环严格地从低到高,所以改为

顶点3被顶点1缩小

1->0
2->1
3->2, 3->1

现在通过顶点2

1->0
2->1
3->2

结果是正确的。我为错误道歉。