我有一个元素数组。该数组按元素的ID排序,但ID是非顺序的,例如ID编号之间存在间隙。
我今天使用二进制搜索来查找特定的ID。
ID为3个字节,大约有1600万个可能性。给定数组中的ID数量要少得多,可能是10000。
这是一个嵌入式/ plc平台,这意味着我无法拥有16MB的查找表,该表占用太多内存。我已经看过这样的位集,但是我不确定这是否是正确的方法,或者如何从中计算出数组偏移。
考虑到我想做一个好的旧的“内存速度”折衷方案,我意识到这可能是一个艰巨的任务,但是我的内存很少,也许可以腾出2MB或更少的空间。但是硬件是固定的。
编辑:对于给定的应用程序,数组的元素是固定的,不能插入或删除数组元素。
如何构建/预计算查找表或类似表以加快查找ID的速度?
谢谢
答案 0 :(得分:2)
我假设二进制搜索太慢。由于该表是固定的,因此在运行时不会有任何添加或删除,您可以看一下“完美的哈希”解决方案。 Wiki很好地解释了https://en.wikipedia.org/wiki/Perfect_hash_function
基本上,在脱机状态下,您需要通过一个完美的哈希生成器来运行表,然后在运行时,通过脱机生成的公式来运行ID,以获取表中项目的索引。
答案 1 :(得分:0)
您只需要具有ID开头的条目的排序表。该代码可以为您创建这些索引,并将该索引与二进制搜索结合使用以进行查找。索引将为40kb。您可能可以节省很多。如果ID仅是3个字节,则可以将其设置为30kb,但这将是不必要的复杂操作,除非您确实短了10kb。
散列可以放弃索引,但是节省空间值得吗?而且,如果条目比它们的ID大得多,那么将不需要那么多空余的表插槽来耗尽节省的空间。
VAR_GLOBAL
entries : ARRAY[1..entryCount] OF ST_Entry := ...; // you need to preinitialize this array here
index: ARRAY[1..entryCount] OF DINT;
_dummy : BOOL := BuildIndex(ADR(index), ADR(entries), entryCount);
END_VAR
VAR_GLOBAL CONSTANT
entryCount : DINT := 10000;
END_VAR
// Called once during PLC initialization only. Returns FALSE always.
FUNCTION BuildIndex : BOOL
VAR_INPUT
index: POINTER TO DINT;
entries : POINTER TO ST_ENTRY;
count : DINT;
END_VAR
WHILE count > 0 DO
index[count] := entries[count].Id;
count := count - 1;
END_WHILE
END_FUNCTION
通过此设置,通过二进制搜索进行索引查找非常容易:
FUNCTION LookupEntry : REFERENCE TO ST_Entry
VAR_INPUT
id : DINT;
END_VAR
VAR
begin : DINT := 1;
mid : DINT;
end : DINT := GVL.entryCount;
midId : DINT;
END_VAR
WHILE TRUE DO
mid := (begin + end) / 2;
midId := index[mid];
IF midId = id THEN
LookupEntry REF= entries[mid];
EXIT;
END_IF
IF mid=begin AND mid=end THEN
EXIT;
END_IF
IF midId < id THEN
begin := mid;
ELSE
end := mid;
END_IF
END_WHILE;
// may return an invalid reference, use of reference will throw
END_FUNCTION