Elixir 中最有效的区间类型搜索

Posted

技术标签:

【中文标题】Elixir 中最有效的区间类型搜索【英文标题】:Most efficient interval type search in Elixir 【发布时间】:2015-03-02 08:55:28 【问题描述】:

我正在开始我的 Elixir 之旅,并正在寻找一些关于如何最好地解决特定问题的建议。

我有一个需要尽快搜索的数据集。数据由两个数字组成,形成一个封闭的波段,以及与每个波段相关的一些元数据。

例如:

From,To,Data    
10000,10999,MetaData1
11000,11999,MetaData2
12000,12499,MetaData3
12500,12999,MetaData4

这个数据集可能有超过 100,000 个条目。

我定义了一个 struct 来对数据进行建模,以及一个在内存中创建 Elixir 列表表示的解析器。

defmodule Band do
    defstruct from: 0, to: 0, metadata: 0
end

解析器返回Bandstruct 的列表。我定义了一个使用列表理解的find 方法

defp find_metadata(bands, number) do
        match? = fn(x) -> x.from <= number and x.to >= number end

        [match | _ ] = for band <- bands, match?.(band), do: band

         :find, band 
end

根据我的新手知识,使用列表推导需要完整遍历列表。为了避免扫描完整列表,我使用了其他语言的搜索树。

Elixir 中是否有可用的算法/机制/方法可以更有效地解决此类搜索问题?

谢谢。

【问题讨论】:

Enum.find/2 可能是您想要的(它会遍历一个列表,直到您找到与给定谓词/函数匹配的第一个)。 感谢@JoséValim,我担心顺序搜索的时间。在 20K 的搜索空间中,运行 5,000 次搜索并在空间末尾进行匹配,列表理解需要约 1400 毫秒,而 Enum.Find 需要约 1800 毫秒。我使用mix test --trace 来获取时间 - 不确定这是否足够准确? 在某种程度上(正如@obrok 在下面建议的那样)这不是 Elixir 问题,而是数据结构问题。选择正确的数据结构,您的性能应该会非常出色。 @OnorioCatenacci 同意 - 希望确保没有一种语言功能可以帮助处理这种类型的数据结构。 【参考方案1】:

如果波段是互斥的,您可以将它们构造成按from 排序的树。搜索该树应该花费log(n) 时间。像下面这样的东西应该可以工作:

defmodule Tree do
  defstruct left: nil, right: nil, key: nil, value: nil

  def empty do
    nil
  end

  def insert(tree, value = key, _) do
    cond do
      tree == nil    -> %Treeleft: empty, right: empty, key: key, value: value
      key < tree.key -> %tree | left: insert(tree.left, value)
      true           -> %tree | right: insert(tree.right, value)
    end
  end

  def find_interval(tree, value) do
    cond do
      tree == nil                -> nil
      value < tree.key           -> find_interval(tree.left, value)
      between(tree.value, value) -> tree.value
      true                       -> find_interval(tree.right, value)
    end
  end

  def between(left, right, value) do
    value >= left and value <= right
  end
end

请注意,您也可以使用Ranges 来存储您所称的“乐队”。另请注意,树不平衡。 (可能)实现平衡树的一个简单方案是在插入间隔之前打乱间隔。否则,您需要一个更复杂的实现来平衡树。你可以看看 erlang 的gb_trees 来获得灵感。

【讨论】:

由于区间似乎形成一个连续体并且数据是排序的,所以我认为搜索值大于或等于下限的第一个条目应该足够了。换句话说,没有必要存储上限并比较value &lt;= right 我不认为这是正确的 - 这种节点的右子树可能包含具有更大left 的区间,但仍小于value。特别考虑一棵只有间隔 1, 1011, 20 的树 - 搜索 15 时停在 1, 10 是不正确的,搜索 100 时停在任一间隔都是不正确的。 谢谢大家,我来实现树形数据结构,看看结果如何。 @obrok 你是对的。必须寻找第一个 满足要求的条目,然后将前一个条目作为结果。但是值超过最后一个区间的情况仍然会产生错误的结果。

以上是关于Elixir 中最有效的区间类型搜索的主要内容,如果未能解决你的问题,请参考以下文章

Elixir模式匹配协议实现者

搜索区间

c# 泛型study

数据库数据类型

类型安全有效地只读 java 集合?

搜索结果中最多两列