在 PostgreSQL 表中,double[] 类型的字段存储高维向量(准确地说是 128 个坐标)。
create table tab (
id integer,
name character varying(200)
vector double precision[]
)
对于给定的向量,您需要从数据库中返回一条记录,其中该向量与表记录中的向量之间的欧几里得距离最小。
有一个函数可以使用众所周知的公式计算两个向量的欧几里得距离SQRT((v1[1]-v2[1])^2+(v1[2]-v2[2])^2+....+(v1[128]-v2[128])^2):
CREATE OR REPLACE FUNCTION public.euclidian(
arr1 double precision[],
arr2 double precision[])
RETURNS double precision AS
$BODY$
select sqrt(SUM(tab.v)) as euclidian from (SELECT
UNNEST(vec_sub(arr1,arr2)) as v) as tab;
$BODY$
LANGUAGE sql IMMUTABLE STRICT
辅助功能:
CREATE OR REPLACE FUNCTION public.vec_sub(
arr1 double precision[],
arr2 double precision[])
RETURNS double precision[] AS
$BODY$
SELECT array_agg(result)
FROM (SELECT (tuple.val1 - tuple.val2)*(tuple.val1 - tuple.val2)
AS result
FROM (SELECT UNNEST($1) AS val1
,UNNEST($2) AS val2
,generate_subscripts($1, 1) AS ix) tuple
ORDER BY ix) inn;
$BODY$
LANGUAGE sql IMMUTABLE STRICT
要求 :
select tab.id as tabid, tab.name as tabname,
euclidian(target_vector,tab.vector) as eucl from tab
order by eulc ASC
limit 1
到目前为止,一切都很好。但不难看出,查询只能通过穷举表中的所有记录来执行。在某种程度上,这适合每个人,但是现在数据库正在增长,并且出现了一个问题,即详尽的搜索并不是很好,委婉地说。现在数据库中有几千条记录,还没有明显的性能问题,但几万条记录已经不远了。
问题 - 在执行查询时,如何设计并在选项卡表中提供索引搜索?这样至少 90% 的不必要记录被索引丢弃,剩下的 10% 已经允许通过完整的枚举。
PS 寻找解决方案的当前方向之一:PostGIS扩展允许您在 3 维空间中按距离搜索和排序 (ST_3DDistance)、按距离过滤 (ST_3DWithin) 等 - 这可以通过索引快速完成。也许有人以某种方式将其抽象为 N 维空间的情况?
PS2。气象观测数据:
- 做了一个查询 select max(val), min(val) from (select unnest(vector) as val from tab) as tab1 - 0.485470712185 -0.41735497117 - 我不确定,我认为坐标值理论上可以不超过 1.0 模
- 虽然向量未归一化,但距 (0,0,...0) 的距离在 1.2 到 1.6 的范围内。
PS3。这不是我第一次解决这样的问题 - 上一次它与在点云 (3D) 中查找最近的点有关,但是我在内存中(iBoxDB 数据库)本地拥有一切,我什至没有尝试摆脱一个完整的枚举......这就是postgresql服务器的全部力量——从我想要解决的原理来看)。
PS4。根据观测数据,点之间的最大距离(基于可用的统计数据)约为 3.2 - 让我们将其扩展为 5,以防万一,或最多 10。我绝对对相距超过 1.0 的点不感兴趣从彼此。让我们从 128 维空间中挑出几个(多少不介意,比如说 10.20)三维投影 - 坐标集 (v1,v2,v3)、(v4,v5,v6) 等。显然,如果在其中一个三维投影中距离超过极限(1.0),那么在全坐标组上它肯定不会变小。接下来,我们尝试应用 PostGIS 中的内容 - 我们索引它们的每个向量的三维投影,然后在搜索时,我们使用 ST_3DWithin < 1.0 设置过滤器(过滤器的数量将等于选择的三个 -尺寸投影)。值得一试的新年前夜:-) 我真的很想从 PostGIS 专家那里得到一个称职的评论 - 奇迹会在除夕发生,还是我们可以玩得开心?:-)
PS5。在水桶的方向上,我建议不要再挖了-首先,无论您如何分散它们,它们都会很多-即使您建立在投影上而不是在全维度上,假设取前64个坐标并将它们分散在花束中(<0,>=0) - 它将起作用 18446744073709551616 可能的花束 - 它在哪里......其次,存在花束边界的问题 - 点可以在最小距离处但落入相邻花束,在查询中必须考虑到这一点,为此,您需要为每个花束保留邻居图...如果您应用最简单的分区 (<0,>=0) - 原则上,它失去了所有意义,因为对于一个轴,只有一个边界,您仍然需要在这里和那里查看...如果您将每个轴分为 4 个范围 - 花束的数量肯定会达到无穷大... 第三,