已编辑:
哦,我现在明白了,“多个”是指“超过两个”,值没有整数标识符,输入行有.好的,让我们重新开始。
您真的需要存储输入吗?如果任何输入为false,则输出为false,因此只要您至少有一个(或两个,如果您愿意)输入为true,并且直到您得到一个输入是false。
哦,但是您说“到达同一输入的两个信号不算作两个”。为了强制执行此操作,您必须跟踪(但不一定存储)输入。这才是真正的归结。
所以真正的问题是:如何在 C# 中有效地存储(和检索)一个稀疏的整数数组?
当您谈论稀疏数组时,字典(哈希表)无疑是显而易见的选择。但是在这种情况下,您只需要为每个条目提供一个布尔值,而Dictionary<int, bool> 确实有些浪费。
输入行 ID 的最大范围是多少?是 [int.MinValue, int.MaxValue],还是在更易于管理的范围内?如果标识符的最大范围相对较小,您可能需要查看System.Collections.BitArray 或System.Collections.Specialized.BitVector32。
如果您使用这些位集合之一,您有两种选择:
- 使用两个 BitArrays/BitVector32s:一个用于存储输入值,另一个用于存储信号是否已到达该线路。
- 仅使用一个 BitArray/BitVector32。使用它来存储信号是否已到达给定行,并保留一个单独的
bool 值,该值将与每个新输入相乘。此选项无法将一行上的 false 输入重置为同一行上的 true 输入。
假设 32 行或更少的输入行,上面选项 1 的有效BitVector32 实现将是这样的:
class AndGate{
BitVector32 activeLines;
BitVector32 inputValues;
public void Reset(){
activeLines[-1] = inputValues[-1] = false;
}
public void Input(int line, bool value){
if(line < 0 || line > 31)
throw new ArgumentOutOfRangeException("line");
activeLines[1 << line] = true;
inputValues[1 << line] = value;
}
public bool GetOutput(bool reset){
bool rtn = activeLines.Data == inputValues.Data;
if(reset)
Reset();
return rtn;
}
}
如果您需要超过 32 行,那么使用 BitArray 的等效实现将是类似的,只是 GetOutput 会更复杂且效率更低。你最好使用BitVector32s(或普通的int/uints)滚动你自己的BitArray等价物。
EDIT2:
鉴于 OP 的反对意见,我只能假设预期的线路 ID 在 [int.MinValue, int.MaxValue] 中,并且线路可以从 false 切换到 true。如果情况确实如此,那么像我上面概述的那样密集的实现是不切实际的。
所以我们回到Dictionary<,>。但是,我们仍然可以对 Dictionary<int, bool> 进行一些优化。
-
一种是改用SortedList<,>。如果可能给出大量输入,SortedList<,> 将使用显着比Dictionary<,> 少的内存。
例如,在Dictionary<int, bool> 中,每个条目将使用至少 17 字节的内存。 SortedList<int, bool> 只会使用 5 个。
最大的缺点1 向SortedList<,> 添加新条目通常比向Dictionary<,> 添加条目要慢得多,因为其他条目的键比较大于添加的条目必须向下移动以便为新条目腾出空间。我建议使用预期输入进行分析,以比较 SortedList<,> 和 Dictionary<,> 之间的内存使用情况和 CPU 烟雾。
另一个优化是将我上面提到的BitVector32方法与Dictionary<,>/SortedList<,>结合起来。这可能2可以防止大量浪费空间来存储 8 位布尔值,以及存储大量键和(如果适用)哈希表开销。
以下是允许这两个概念的示例实现:
class AndGate{
struct AndEntry{
BitVector32 activeLines;
BitVector32 inputValues;
public bool Output{get{return activeLines.Data == inputValues.Data;}}
public void Input(int line, bool value){
activeLines[1 << line] = true;
inputValues[1 << line] = value;
}
}
IDictionary<int, AndEntry> entries;
public AndGate(bool useSortedList){
if(useSortedList)
entries = new SortedList<int, AndEntry>();
else entries = new Dictionary<int, AndEntry>();
}
public void Reset(){entries.Clear();}
public bool Input(int line, bool value){
AndEntry entry;
entries.TryGetValue(line / 32, out entry);
/* no need to handle the not found case, since AndEntry is a struct */
entry.Input(line & 31, value);
entries[line / 32] = entry;
}
public bool GetOutput(bool reset){
bool rtn = true;
foreach(AndEntry value in entries.Values)
if(!value.Output){
rtn = false;
break;
}
if(reset)
Reset();
return rtn;
}
}
请记住,这些优化的唯一好处是使用更少的内存。这仅在您期望 许多 输入时才重要。 “很多”的确切含义很难确定,但对于简单的Dictionary<int, bool> 实现,将每个条目称为 20 个字节(以考虑开销)。因此,将您愿意使用的内存量除以 20。这就是“许多”。但要注意 CPU 和内存之间的权衡。
1 在有人天真地争论排序列表相对于哈希表的真正缺点之前(Dictionary<,> 的实现方式)是排序列表中的查找速度较慢哈希表,不要。 “但是在排序列表中查找是 O(log N),而在哈希表中只有 O(1)”,他们会说。哎呀,吓坏了。一方面,哈希表可能会降级到 O(N),而排序列表总是 O(log N)。对于两个,即使你有十亿个项目,30 个整数比较(就像在这种情况下一样)也不会花费那么多。对于哈希表开销,许多人惊讶地发现排序列表在查找时优于哈希表的频率。
2 同样,这取决于您的输入。如果lineID & ~31 不经常重叠(所以大多数AndEntry 对象最终只存储一行),那么这个解决方案将使用更多 内存,而不是更少。相反,如果 lineID 中的其他一些 27 位集合倾向于重叠,那么 Input() 中的不同掩码会更有效。