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
解析器返回Band
struct
的列表。我定义了一个使用列表理解的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 <= right
。
我不认为这是正确的 - 这种节点的右子树可能包含具有更大left
的区间,但仍小于value
。特别考虑一棵只有间隔 1, 10
和 11, 20
的树 - 搜索 15 时停在 1, 10
是不正确的,搜索 100 时停在任一间隔都是不正确的。
谢谢大家,我来实现树形数据结构,看看结果如何。
@obrok 你是对的。必须寻找第一个 不 满足要求的条目,然后将前一个条目作为结果。但是值超过最后一个区间的情况仍然会产生错误的结果。以上是关于Elixir 中最有效的区间类型搜索的主要内容,如果未能解决你的问题,请参考以下文章