Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
俺の kd-tree
Search
Terada Yuichiro
November 24, 2020
Programming
0
1k
俺の kd-tree
Rust で kd-tree を自作し、crates.io に公開しました。kd tree とは?パフォーマンスは? 設計は? といったあたりをまとめた発表資料です。
Terada Yuichiro
November 24, 2020
Tweet
Share
More Decks by Terada Yuichiro
See All by Terada Yuichiro
WebGPU 触ってみた
u1roh
0
1.8k
【STUDDi】WebGL で学ぶ 3D Graphics の概略
u1roh
0
1.3k
Graph in Rust with unsafe
u1roh
2
1.2k
Other Decks in Programming
See All in Programming
社内フレームワークとその依存性解決 / in-house framework and its dependency management
vvakame
1
550
Pythonでもちょっとリッチな見た目のアプリを設計してみる
ueponx
1
530
AWSマネコンに複数のアカウントで入れるようになりました
yuhta28
2
160
2024年のWebフロントエンドのふりかえりと2025年
sakito
1
240
sappoRo.R #12 初心者セッション
kosugitti
0
240
時計仕掛けのCompose
mkeeda
1
290
Java Webフレームワークの現状 / java web framework at burikaigi
kishida
9
2.2k
Multi Step Form, Decentralized Autonomous Organization
pumpkiinbell
1
720
第3回関東Kaggler会_AtCoderはKaggleの役に立つ
chettub
3
950
ISUCON14公式反省会LT: 社内ISUCONの話
astj
PRO
0
190
『GO』アプリ データ基盤のログ収集システムコスト削減
mot_techtalk
0
120
Conform を推す - Advocating for Conform
mizoguchicoji
3
690
Featured
See All Featured
Gamification - CAS2011
davidbonilla
80
5.1k
Embracing the Ebb and Flow
colly
84
4.6k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
114
50k
Fantastic passwords and where to find them - at NoRuKo
philnash
51
3k
RailsConf 2023
tenderlove
29
1k
GraphQLとの向き合い方2022年版
quramy
44
13k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
27
1.9k
Art, The Web, and Tiny UX
lynnandtonic
298
20k
What's in a price? How to price your products and services
michaelherold
244
12k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.1k
Scaling GitHub
holman
459
140k
Transcript
俺の kd-tree 2020-11-24 Y.Terada @ CADDi
自己紹介 • てらだ( @u_1roh ) • CADDi で図面の画像解析 に取り組んでいます。 •
Rust でアルゴリズム、い いですよね!
こんなことを話します • kd-tree を自作して crates.io に publish してみた • そもそも
kd-tree とは? • なぜ自作したの? • どんな実装? • パフォーマンスは?
kd-tree とは • k-dimensional tree • k 次元空間の点群を高速に探索可能にするデータ構造 • こんな探索が出来ます
– 【最近傍探索】点群から、指定座標に最も近い点を探索 – 【 k 近傍探索】近いものから k 個探索( k が被ってる…) – 【領域探索】指定領域に含まれる点を全て探索
kd-tree の構築( 2D の場合) • 2 分木のノードに点をひとつ格納 • X で分割
→ Y で分割 → X で分割 → Y で分割 → … https://www.datasciencecentral.com/profiles/blogs/implementing-kd-tree-for-fast-range-search-nearest-neighbor
最近傍探索 問合せ点 Q この枝における 最近傍点 探索不要な枝 探索済みの枝
crates.io を探すと
使ってみた • `kdtree` – 遅い – 点の逐次追加でツリーを構築するスタイル – 任意次元の kd
tree が作れる – 点座標の数値型は `num_traits::Float` 型(つまり `f64` と `f32`) – 型が `KdTree<f64, T, [f64; 3]>` みたいになる(`f64` を 2回書く) • `fux_kdtree` – 速い( `kdtree` の 2.5〜 3倍) – 点の配列から一気に構築するスタイル – 3次元までしか作れない – 点座標の数値型は `f64` のみ – `fux_kdtree::kdtree::KdtreePointTrait` を実装しないといけない
作りたいもの • `fux_kdtree` 並の速度で、 • 任意次元で使えて、 • 整数値の座標値が扱えて、 • 設計が洗練されているもの
できた!
ベンチマーク(構築処理) fux_kdtree kdtree 俺の (f64) 俺の (i32) [0, 1]^3 の空間に
一様な乱数で 10^n 個の点を生成 10^2 点 10^3 点 10^4 点
ベンチマーク(最近傍探索) fux_kdtree kdtree 俺の (f64) 俺の (i32) 10^2 点 10^4
点 10^3 点
機能はまだ足りない • 最近傍探索しかできない – ◦ 【最近傍探索】指定座標に最も近い点を探索 – ☓ 【 k
近傍探索】近いものから k 個探索 – ☓ 【領域探索】指定領域に含まれる点を全て探索 • 今後、機能追加していきたい
俺の kd-tree 使い方 編
基本のき use kd_tree::KdTree; let items: Vec<[i32; 3]> = vec![[1, 2,
3], [3, 1, 2], [2, 3, 1]]; let kdtree: KdTree<[i32; 3]> = KdTree::build(items); assert_eq!(kdtree.nearest(&[3, 1, 2]).unwrap().item, &[3, 1, 2]); • KdTree<[i32; 3]> ← 型がシンプル! • 整数座標が使える!(※ unsigned はダメ)
f64 の座標を使う • 浮動小数点座標の場合は build_by_ordered_float() を使う。 – f64 は Ord
を実装していないので。 – ordered_float::OrderedFloat を利用 use kd_tree::KdTree; let kdtree: KdTree<[f64; 3]> = KdTree::build_by_ordered_float(vec![ [1.0, 2.0, 3.0], [3.0, 1.0, 2.0], [2.0, 3.0, 1.0] ]); assert_eq!( kdtree.nearest(&[3.1, 0.9, 2.1]).unwrap().item, &[3.0, 1.0, 2.0] );
マップのようにも使えます • KdMap<P,T> … Map のように使える kd tree – P
に点座標 – T には任意の型が入る use kd_tree::KdMap; let kdmap: KdMap<[isize; 3], &'static str> = KdMap::build(vec![ ([1, 2, 3], "foo"), ([2, 3, 1], "bar"), ([3, 1, 2], "buzz"), ]); assert_eq!(kdmap.nearest(&[3, 1, 2]).unwrap().item.1, "buzz");
KdPoint カスタム実装 use kd_tree::{KdPoint, KdTree}; struct Item { point: [f64;
2], id: usize } impl KdPoint for Item { type Scalar = f64; type Dim = typenum::U2; // 2 dimensional tree. fn at(&self, k: usize) -> f64 { self.point[k] } } let kdtree: KdTree<Item> = KdTree::build_by_ordered_float(vec![ Item { point: [1.0, 2.0], id: 111 }, Item { point: [2.0, 3.0], id: 222 }, Item { point: [3.0, 4.0], id: 333 }, ]); assert_eq!(kdtree.nearest(&[1.9, 3.1]).unwrap().item.id, 222);
KdSlice • KdTree のスライス型( String に対する str ) – Sized
ではない → 常に参照型で扱う – KdTree は Deref<Target=KdSlice<...>> を実装 • KdSlice::sort() は items を move しない(ソートするだ け) use kd_tree::KdSlice; let mut items: Vec<[i32; 3]> = vec![[1, 2, 3], [3, 1, 2], [2, 3, 1]]; let kdtree: &KdSlice<[i32; 3]> = KdSlice::sort(&mut items); assert_eq!(kdtree.nearest(&[3, 1, 2]).unwrap().item, &[3, 1, 2]);
俺の kd-tree データ構造 編
バランス良く構築 8 個 8 個 4 個 3 個 4
個 3 個
構築フロー items: &mut [T] x 座標でソート ↑items.len()/2 y 座標でソート ↑items.len()/2
x 座標でソート ↑items.len()/2 このまま実装すると ソートが多いので遅い
Quick select • 必要なのはソートではない – 中央値の左右で要素が分かれていれば良い – 左右それぞれの枝はソートされている必要がない • Quick
select • `pdqselect` クレート • k 番目を境に要素を分ける
`kd_sort_by()`
並び替えるだけで kd tree になる • binary-tree と似ている – [T] を
sort() しておけば binary_search() できる – 同様に、 [T] を並び替えておけば最近傍探索できる • 構築に必要なのは次元と kd_compare だけ – (注:最近傍探索は kd_compare だけでは足りません。距離が測 れないといけないので。) • このシンプルさ&柔軟さを最大限に活かした設計にしたい。
KdSliceN<T, N> pub struct KdSliceN<T, N: Unsigned>(PhantomData<N>, [T]); impl<T, N:
Unsigned> KdSliceN<T, N> { pub fn sort_by<F>(items: &mut [T], compare: F) -> &Self where F: Fn(&T, &T, usize) -> Ordering + Copy, { kd_sort_by(items, N::to_usize(), compare); unsafe { &*(items as *const _ as *const Self) } } } 次元 unsized 参照を返す unsafe を使ってキャスト
KdTreeN<T, N> pub struct KdTreeN<T, N: Unsigned>(PhantomData<N>, Vec<T>); impl<T, N:
Unsigned> KdTreeN<T, N> { pub fn build_by<F>(mut items: Vec<T>, compare: F) -> Self where F: Fn(&T, &T, usize) -> Ordering + Copy, { kd_sort_by(&mut items, N::to_usize(), compare); Self(PhantomData, items) } }
KdPoint
KdPoint の実装
Type aliases pub type KdSlice<T> = KdSliceN<T, <T as KdPoint>::Dim>;
pub type KdTree<T> = KdTreeN<T, <T as KdPoint>::Dim>; pub type KdMap<P, T> = KdTree<(P, T)>; pub type KdMapSlice<P, T> = KdSlice<(P, T)>;
まとめ • 自作 kd-tree を crates.io に公開した • 性能 –
`kdtree` より速い(構築も探索も) – 探索は `fux_kdtree` より速い • 機能 – 整数座標が扱える – 高次元が扱える ※ CADDi では Rust でアルゴリズムを書きたいエンジニアを募集しています
None