[英]C# Cloning graph and updating circular references
Let me preface this by stating that I have seen similar posts to this, but none of the solutions have satisfied me and/or applied to C#.让我先说明我看过类似的帖子,但没有一个解决方案让我满意和/或适用于 C#。
I have a Graph
class that consists of Node
and Connection
objects.我有一个由
Node
和Connection
对象组成的Graph
类。 The graph contains collections consisting of all of the child Node and Connection objects associated with it.该图包含由与其关联的所有子 Node 和 Connection 对象组成的集合。 In addition to this, each Node has a collection of Connection objects.
除此之外,每个节点都有一个 Connection 对象的集合。
Please note: This is a simplified toy problem.请注意:这是一个简化的玩具问题。 You can view the actual (work-in-progress) production code here .
您可以在此处查看实际(正在进行的)生产代码。 In production, a
Neuron
is a Node
and an Axon
is a Connection
.在生产中,
Neuron
是Node
, Axon
是Connection
。
public class Graph : IDeepCloneable<Graph>
{
// These are basically just Dictionary<int,object>s wrapped in an ICollection
public NodeCollection Nodes;
public ConnectionCollection Connections;
public Graph Clone()
{
return new Graph
{
Nodes = this.Nodes.Clone(),
Connections = this.Connections.Clone()
};
}
}
public class Node : IDeepCloneable<Node>
{
public int Id;
public NodeConnectionCollection Connections;
// NodeConnectionCollection is more or less the same as NodeCollection
// except that it stores Connection objects into '.Incoming' and '.Outgoing' properties
public Node Clone()
{
return new Node
{
Id = this.Id,
Connections = this.Connections.Clone()
};
}
}
public class Connection : IDeepCloneable<Connection>
{
public int Id;
public Node From;
public Node To;
public Connection Clone()
{
return new Connection
{
Id = this.Id,
From = this.From.Clone(),
To = this.To.Clone()
};
}
}
public class ConnectionCollection : ICollection<Connection>, IDeepCloneable<ConnectionCollection>
{
private Dictionary<int, Connection> idLookup;
private Dictionary<ProjectionKey, Connection> projectionLookup;
public int Count => idLookup.Count;
public bool IsReadOnly => false;
public Add( Connection conn )
{
idLookup.Add( conn.Id, conn );
projectionLookup.Add( new ProjectionKey( conn.From, conn.To ), conn );
}
...
internal struct ProjectionKey
{
readonly intFrom;
readonly int To;
readonly int HashCode;
public ProjectionKey( int from, int to )
{
From = from;
To = to;
HashCode = ( 23 * 397 + from ) * 397 + to;
}
public override int GetHashCode() { return HashCode; }
}
}
public class NodeCollection : ICollection<Node>, IDeepCloneable<NodeCollection>
{
private Dictionary<int, Node> nodes;
private Dictionary<int, InputNode> inputNodes;
private Dictionary<int, InnerNode> innerNodes;
private Dictionary<int, OutputNode> outputNodes;
...
public Node this[ int id ]
{
get => nodes[ id ];
}
}
Each of these objects support deep cloning, with the main idea being that consuming classes can call Clone()
on child classes and work down the stack that way.这些对象中的每一个都支持深度克隆,主要思想是消费类可以在子类上调用
Clone()
并以这种方式在堆栈中工作。
However, this is not viable in production.然而,这在生产中是不可行的。 A call to
Graph.Clone()
will clone the NodeCollection
and ConnectionCollection
fields, which will clone each Node
and Connection
instance stored within them, which will each clone other referencing child elements.调用
Graph.Clone()
将克隆NodeCollection
和ConnectionCollection
字段,这将克隆存储在其中的每个Node
和Connection
实例,这将分别克隆其他引用子元素。
A common solution seems to be storing the Ids of each child object and then rebuilding the references when all cloning is complete.一个常见的解决方案似乎是存储每个子对象的 Id,然后在所有克隆完成后重建引用。 However, as far as I am aware, this would require a reference to parent objects and tightly couple the data structure.
但是,据我所知,这需要对父对象的引用并紧密耦合数据结构。
I am very puzzled at how to properly approach this.我对如何正确处理这个问题感到非常困惑。 I require a reasonable amount of performance , as my application (a genetic algorithm) performs cloning constantly, but in this case I am more interested in finding a robust design pattern or implementation that will allow me to perform deep cloning of this graph structure while stashing a lot of the gruntwork behind the scenes.
我需要合理的性能,因为我的应用程序(遗传算法)不断执行克隆,但在这种情况下,我更感兴趣的是找到一个健壮的设计模式或实现,这将允许我在隐藏时执行此图结构的深度克隆很多幕后工作。
Is there any design pattern that will allow me to clone this data structure as-is while updating circular references and maintaining its integrity?是否有任何设计模式可以让我在更新循环引用并保持其完整性的同时按原样克隆此数据结构?
My suggestion would be to change your approach to the problem from cloning to recreating.我的建议是将您解决问题的方法从克隆改为重新创建。 I've dealt with a resembling problem, where I was saving a graph user created manually from the user interface, and then upon an import of saved graph I was recreating it.
我处理了一个类似的问题,我正在保存从用户界面手动创建的图形用户,然后在导入保存的图形时重新创建它。 It sounds almost the same if you think about it.
如果你仔细想想,这听起来几乎是一样的。
So the solution I came up with was serializing the graph from a central control (considering you are modifying graphs with an heuristic I assume you have central control over the graph).所以我想出的解决方案是从中央控件序列化图形(考虑到您正在使用启发式修改图形,我假设您对图形具有中央控制权)。 Even if you don't have a central control over the graph I believe it can be traversed in a way to get all the information.
即使您没有对图形的中央控制,我相信也可以通过某种方式遍历它以获取所有信息。
In the simplest form a graph is a collection of neighborhood information.在最简单的形式中,图是邻域信息的集合。
Can be directed or undirected as well也可以是有向或无向的
1 -> 2
1 -> 3
3 -> 2
So if you can come up with a way to generate a list like this, after just tweaking this simple list, you can create your new graph.因此,如果您能想出一种方法来生成这样的列表,只需调整这个简单列表后,您就可以创建新图表。
Or another approach would be to list your nodes with their neighbors like below,或者另一种方法是列出您的节点及其邻居,如下所示,
1, [2,3]
3, [2]
This would even be simpler to recreate the graph in my opinion.在我看来,重新创建图表会更简单。
Here is the file from the project I applied this approach if you are curious about - I don't think it would be a reference for the answer or question though.如果您对此感到好奇,这是我应用此方法的项目中的文件- 但我认为它不会作为答案或问题的参考。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.