查找 table 以查找已排序的非顺序元素
Lookup table for sorted non-sequential elements
我有一个元素数组。该数组按元素的 ID 排序,但 ID 是非连续的,例如身份证号码有差距。
我今天使用二进制搜索来查找特定 ID。
ID是3个字节,大概有1600万种可能。给定数组中 ID 的数量要少得多,可能只有 10 000 个。
这是一个 embedded/plc 平台,这意味着我无法进行 16MB 的查找 table,这会占用太多内存。我看过这样的位集,但我不确定这是否是正确的方法,或者如何从中计算数组偏移量。
我意识到这可能是一个艰难的过程,因为我想做旧的 "speed for memory" 权衡,但我的内存很少,可能只有 2MB 或更少的内存可供使用。但是硬件是固定的。
编辑:对于给定的应用程序,数组的元素是固定的,不能插入或删除数组元素。
我如何build/precompute查找table或类似的方法来加快查找 ID 的速度?
谢谢
我假设二进制搜索太慢了。由于table是固定的,所以在运行期间不会有增删,可以看看"perfect hash"的解决办法。 Wiki 有一篇非常好的文章对此进行了解释 https://en.wikipedia.org/wiki/Perfect_hash_function
基本上,离线你需要通过一个完美的哈希生成器运行 table,然后在运行期间你通过离线生成的公式运行 ID获取 table.
中项目的索引
您只需要已排序的 table 个以 ID 开头的条目。该代码可以为您创建一个索引,并使用索引和二进制搜索进行查找。索引将为 40kb。你可能会节省那么多。如果 ID 真的只有 3 个字节,它可以变成 30kb,但除非你真的短 10kb,否则这将是一个不必要的复杂化。
散列可以放弃索引,但是 space 的节省值得吗?如果条目比它们的 ID 大得多,那么不需要那么多空位 table 就可以用完节省的空间。
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
我有一个元素数组。该数组按元素的 ID 排序,但 ID 是非连续的,例如身份证号码有差距。
我今天使用二进制搜索来查找特定 ID。
ID是3个字节,大概有1600万种可能。给定数组中 ID 的数量要少得多,可能只有 10 000 个。
这是一个 embedded/plc 平台,这意味着我无法进行 16MB 的查找 table,这会占用太多内存。我看过这样的位集,但我不确定这是否是正确的方法,或者如何从中计算数组偏移量。
我意识到这可能是一个艰难的过程,因为我想做旧的 "speed for memory" 权衡,但我的内存很少,可能只有 2MB 或更少的内存可供使用。但是硬件是固定的。
编辑:对于给定的应用程序,数组的元素是固定的,不能插入或删除数组元素。
我如何build/precompute查找table或类似的方法来加快查找 ID 的速度?
谢谢
我假设二进制搜索太慢了。由于table是固定的,所以在运行期间不会有增删,可以看看"perfect hash"的解决办法。 Wiki 有一篇非常好的文章对此进行了解释 https://en.wikipedia.org/wiki/Perfect_hash_function
基本上,离线你需要通过一个完美的哈希生成器运行 table,然后在运行期间你通过离线生成的公式运行 ID获取 table.
中项目的索引您只需要已排序的 table 个以 ID 开头的条目。该代码可以为您创建一个索引,并使用索引和二进制搜索进行查找。索引将为 40kb。你可能会节省那么多。如果 ID 真的只有 3 个字节,它可以变成 30kb,但除非你真的短 10kb,否则这将是一个不必要的复杂化。
散列可以放弃索引,但是 space 的节省值得吗?如果条目比它们的 ID 大得多,那么不需要那么多空位 table 就可以用完节省的空间。
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