我有一个代表大电路网络的图表。每条电线/设备都可以是“活动的”(它是 - 通过一些电线和设备的路径 - 连接到电源)或“不活动的”(它没有连接到电源)。没有对地线或电压进行建模。它要么是活跃的,要么是不活跃的。
有不同种类的设备。设备可以有任意数量的插头。插头通过电线将设备连接在一起。电线的确切布局并不重要。重要的是哪些插头连接在一起。
一些设备示例:
- 灯
用于方便地显示电路的某些状态。它有一个控制灯状态的插头。如下面的屏幕截图所示,活动设备和电线以红色显示,不活动以蓝色显示。 - 电源
始终处于活动状态。它有一个始终输出信号的插头。因此,连接到该插头的电线将始终处于活动状态,连接到该电线的设备也将变为活动状态。 - Input
表示与外界交互的某种方式。可以由运行模拟的进程设置为活动或不活动。它将在其唯一的插头上反映其状态。 - 继电器
这个设备做一些更复杂的事情。它由多个部分组成:它有一个线圈插头,用于控制实际继电器中线圈的状态。它有任意数量的由继电器状态控制的开关。每个都有三个插头,如SPDT开关。当继电器未激活时,来自常闭插头的输入信号将被转发到公共插头,反之亦然,来自常开插头的信号将不会被转发。当继电器处于活动状态时,它会连接公共和常开插头。
我想尽可能快地浏览这个图表。遍历前图中设备和线路的状态为“当前代”。遍历图计算下一代的状态。遍历完成后,设备和电线的状态被设置为新状态。更新状态分两部分完成(计算状态并应用它们)以避免出现问题,例如中继可能更改其状态并立即影响遍历。就像您运行 Game of Life 时一样,但不是将结果写入新数组并在计算完所有内容后将所有内容复制回来,而是逐渐覆盖上一代的数据并创建错误的结果。
遍历基本上应该这样做:
- 首先假设所有对象(设备、插头、电线)在下一代都将处于非活动状态。当遍历到达它们时,它们将被标记为活动的。
- 然后找到所有肯定通电的插头。像电源插头,以及其他可以输出信号的设备。这些是遍历的起点。
- 如果它已被标记为活动,请跳过它。遍历可以多次到达对象。如果这种情况不会被捕获,它将导致无限循环/递归。
- 将其标记为活动。
- 通知它所连接的设备该插头已激活。根据它在什么设备上的插头,做一些事情。例如,继电器开关之一上的公共插头。如果继电器处于活动状态,则对常开插头递归,否则对常闭插头递归
- 通知连接的电线,将其标记为活动并递归连接到电线的所有其他插头。
- 当所有起点都完成后,将“下一代”状态分配给每个对象的实际状态。
这是一个简单电路的例子。起初一切都处于非活动状态:
在下一代网络中,左侧的有线网络、顶部的导线和左侧的灯都变为活动状态,因为它们以某种方式连接到电源:
这里我激活了底部的输入:
在下一代中,继电器变为活动状态。注意右侧的电线没有改变。计算这一代时,继电器仍处于非活动状态:
在下一代中,电线确实发生了变化:
当前存在的图没有针对快速遍历进行优化。它针对易于编辑和显示更详细的信息进行了优化,例如红线上的小箭头,它们显示了信号的来源。并且有很多“不必要的”步骤可以优化。有些设备只是将电线连接到电路不同部分的其他电线。这仅适用于光学,因为它使电路的哪些部分形成逻辑单元更加清晰。而且因为电线需要易于编辑,所以每个弯曲或交叉点都是一个设备,它在功能上什么都不做,但可以在屏幕上拖动。上面屏幕截图左侧的有线网络实际上是 4 根独立的电线和 2 个这些伪设备。
正因为如此,它太慢了。凭借我拥有的最大网络,我每秒最多可以生成 3 代。
我正在编写一个程序,它通过设置输入、计算生成直到电路稳定并确保电路处于预期状态来自动化电路的一些测试。使用现有的解决方案太慢了。
图表的初始设置以及需要做的任何事情都可能很慢,因为它只会做一次,之后图表的结构永远不会改变。但是计算世代应该非常快。还应该有一种方法可以检测到没有任何变化,因为我不提前知道电路的信号延迟。
图表应该包含哪些数据以及我可以使用哪些技巧来优化速度?(高内存使用根本不是问题)
我如何在多个线程上分配负载?做单线程很容易,但我如何确保多个线程不会做一些愚蠢的事情,而不会因为到处乱扔锁而破坏性能?也许可以在设置阶段确定图表的哪些部分应该由哪个线程完成?
我使用 .NET (Framework 4.0),但任何一般提示都值得赞赏。