查找 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