web-dev-qa-db-ja.com

n次元空間で最も類似したオブジェクトのクイック検索

N次元空間に点があると仮定しましょう。したがって、各ポイントの位置を表すことができるn個の座標(n列)があります。

最も類似したポイント、つまり目的のポイントまでの距離が最小のポイントをすばやく検索するために使用できるテーブルを実装する必要があります。

例えば。データベース内のポイント:

_id  c1  c2  c3  c4  c5
1   5   19  42  12  16
2   3   23  38  15  12
3   14  21  32  33  1
4   12  29  21  24  5
_

座標のあるポイントに最も一致するものを見つけたい場合:

_c1 c2  c3  c4  c5
4  20  40  14  15
_

ID 1および2のポイントを取得します。

また、各次元(列)の平均座標と各ポイントのベクトルがあります。最初の要素-ポイントがこの次元の平均座標との差が最も大きい次元の番号、および最後-そのポイントの次元の数最小の違いがあります。おそらく、目的のポイントまでの距離が最も大きい、より高速なフィルタリングポイントに使用できます。

MySQLを使用してこのようなことをするにはどうすればよいですか?

複合インデックスとorder by abs(cx - $mycx)は良い解決策になると思いますが、1つのインデックスに含める必要のある16を超える列があるため、使用できません。

どんな助けも非常に役に立ちます!

3
don-prog

cube を使用するPostgreSQL

PostgreSQLを使用すると、これは 他の質問で説明したcube拡張子 ..サンプルデータでかなり簡単になります。

CREATE TABLE foo AS
SELECT *
FROM ( VALUES
  (1, cube(ARRAY[5 , 19, 42, 12, 16]) ),
  (2, cube(ARRAY[3 , 23, 38, 15, 12]) ),
  (3, cube(ARRAY[14, 21, 32, 33, 1 ]) ),
  (4, cube(ARRAY[12, 29, 21, 24, 5 ]) )
) AS t(id, c);

距離を求めています...

SELECT
  id,
  c,
  c2,
  cube_distance(c,c2),
  rank() OVER (ORDER BY cube_distance(c,c2))
FROM foo
CROSS JOIN ( SELECT cube(ARRAY[4,20,40,14,15]) )
  AS t(c2)
ORDER BY cube_distance(c, c2);

出力。

 id |          c          |         c2          |  cube_distance   | rank 
----+---------------------+---------------------+------------------+------
  1 | (5, 19, 42, 12, 16) | (4, 20, 40, 14, 15) |  3.3166247903554 |    1
  2 | (3, 23, 38, 15, 12) | (4, 20, 40, 14, 15) | 4.89897948556636 |    2
  4 | (12, 29, 21, 24, 5) | (4, 20, 40, 14, 15) | 26.5706605111728 |    3
  3 | (14, 21, 32, 33, 1) | (4, 20, 40, 14, 15) | 26.8700576850888 |    4
3
Evan Carroll

プランA:(これ以上の情報がない場合、私はこのソリューションを好みます。)

_ORDER BY 
    ABS(c1 - $c1) +
    ABS(c2 - $c2) +
    ABS(c3 - $c3) +
    ABS(c4 - $c4) +
    ABS(c5 - $c5)  ASC
_

または「二乗平均平方根」(不要なSQRTなし):

_ORDER BY 
    (c1 - $c1) * (c1 - $c1) +
    (c2 - $c2) * (c2 - $c2) +
    ...  ASC
_

singleクエリ($ c1、$ c2、...)に最も近いアイテムを見つけるには、データを1回パスします。データが巨大な場合、I/Oバウンドになるため、ディスク速度が支配的な制約になります。正方形はABS()よりもごくわずかに遅くなるだけなので、希望するメトリックを選択します。 (注:POW(, 2)の使用は遅くなることがあります。)

このクエリで使用するインデックスはありません。

_LIMIT 10_を追加して、「最も近い」10を取得できます。正確に同じ(そして最も近い)メトリックを持つものすべてを検索したい場合、コードはかなり面倒で遅くなります。

プランB:数値が「適切に機能する」場合、1つの列でインデックスを使用できる可能性があります。その場合、まずその列の値に「近い」行を見つけます。 (このリストはテーブルの10%を超えてはなりません。)次に、上記のコードを使用して、行のサブセットを手間をかけて調べます。リスクは、other列が非常に近い可能性がある一方で、this列は遠すぎるため、最初のパスでフィルター処理できないことです。

プランCは nearest pizza parlors を見つけるのに役立ちますが、2次元を超えることはありません。

1
Rick James