已排序的非顺序元素的查找表

时间:2018-12-14 13:31:30

标签: arrays search embedded lookup iec61131-3

我有一个元素数组。该数组按元素的ID排序,但ID是非顺序的,例如ID编号之间存在间隙。

我今天使用二进制搜索来查找特定的ID。

ID为3个字节,大约有1600万个可能性。给定数组中的ID数量要少得多,可能是10000。

这是一个嵌入式/ plc平台,这意味着我无法拥有16MB的查找表,该表占用太多内存。我已经看过这样的位集,但是我不确定这是否是正确的方法,或者如何从中计算出数组偏移。

考虑到我想做一个好的旧的“内存速度”折衷方案,我意识到这可能是一个艰巨的任务,但是我的内存很少,也许可以腾出2MB或更少的空间。但是硬件是固定的。

编辑:对于给定的应用程序,数组的元素是固定的,不能插入或删除数组元素。

如何构建/预计算查找表或类似表以加快查找ID的速度?

谢谢

2 个答案:

答案 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