我正在使用有限自动机实现专用的正则表达式引擎。我将不得不存储数千个状态,每个状态都有自己的转换表,从unicode代码点(或UTF-16代码单元;我还没有决定)到状态ID。
在许多情况下,表格将非常稀疏,但在其他情况下,它几乎已满。在大多数情况下,大多数条目将分为几个具有相同值的连续范围。
最简单的实现是查找表,但每个这样的表都会占用大量空间。 (范围,值)对的列表会小得多,但速度会慢一些。二叉搜索树比列表更快。
是否有更好的方法,可能利用内置功能?
答案 0 :(得分:1)
听起来你有两种非常不同的情况(“在很多情况下,表格会非常稀疏,但在其他情况下它会快满了”。)
对于稀疏情况,您可能有一个单独的稀疏索引(或多个索引层),然后您的实际数据可以存储在一个类型化数组中。因为索引将从整数映射到整数,所以它们也可以表示为类型化数组。
查找值看起来像这样:
我无法想到在JavaScript中使用更好的东西。类型化数组非常快,索引应该会大大提高速度。话虽这么说,如果你只有几千个条目,不要打扰索引,直接在类型数组上进行二进制搜索(在上面的4.中描述)。
对于密集的情况,我不确定。如果密集情况恰好是可能存在跨键范围的重复值的情况,请考虑使用类似run-length encoding的内容 - 相同的连续值仅表示为它们的出现次数,然后表示实际值。再一次,使用类型化数组和二进制搜索,甚至可能使用索引来加快速度。
答案 1 :(得分:1)
不幸的是,JavaScript的内置数据类型 - 尤其是Map
- 对完成此任务没有太大帮助,因为它们缺乏相关的方法。
在大多数情况下,大多数条目都会分成几个连续的 具有相同值的范围。
我们可以利用它并在排序数组上使用二进制搜索策略,假设转换表不会经常修改。
通过将每个输入范围的最低值存储在已排序的数组中,对导致相同状态的连续输入范围进行编码。将相应索引处的状态保持在单独的数组中:
let inputs = [0, 5, 10]; // Input ranges [0,4], [5,9], [10,∞)
let states = [0, 1, 0 ]; // Inputs [0,4] lead to state 0, [5,9] to 1, [10,∞) to 0
现在,给定输入,您需要在输入数组上执行类似于Java's floorEntry(k)的二进制搜索:
// Returns the index of the greatest element less than or equal to
// the given element, or undefined if there is no such element:
function floorIndex(sorted, element) {
let low = 0;
let high = sorted.length - 1;
while (low <= high) {
let mid = low + high >> 1;
if (sorted[mid] > element) {
high = mid - 1;
} else if (sorted[mid] < element) {
low = mid + 1;
} else {
return mid
}
}
return low - 1;
}
// Example: Transition to 1 for emoticons in range 1F600 - 1F64F:
let transitions = {
inputs: [0x00000, 0x1F600, 0x1F650],
states: [0, 1, 0 ]
};
let input = 0x1F60B; //
let next = transitions.states[floorIndex(transitions.inputs, input)];
console.log(`transition to ${next}`);
此搜索在O(log n)步骤中完成,其中n是连续输入范围的数量。然后,单个状态的转换表的空间要求为O(n)。只要我们的初始假设 - 导致相同状态的连续输入范围的数量很小 - 这种方法对于稀疏和密集转换表同样有效。