メインコンテンツまでスキップ

座標圧縮

座標圧縮とは

要素の前後で変化起きないのであれば、取り除いて圧縮する操作を座標圧縮という。

例えば、

[100, 2, 10, 550, 4, 93]

のような配列があった場合に、配列の値そのものではなく大小関係のみが必要である場合に100, 550のような大きな数字は不必要であると言える。 この配列を

[4, 0, 2, 5, 1, 3]

のように変換することで小さな数として扱うことができる。

座標圧縮

アルゴリズム

アルゴリズムはシンプルで、

  1. 圧縮した配列をコピーし、配列の重複を削除
  2. 重複を削除した配列をソート
  3. 二分探索を用いて位置を求める

だけである。

プログラム

coordinate-compression.py
def compress(input):
vals = []
for i in range(len(input)):
vals.append(input[i])
return sorted(set(vals))


a = [100, 2, 10, 550, 4, 93]
compress_a = {v: i for i, v in enumerate(compress(a))}
compressed_a = [compress_a[i] for i in a]
備考

Python の場合は辞書型で O(1)で求めることができるので 2 分探索を使用せずに圧縮を行なっている。 C++では map 型が Python の辞書型に相当するが、O(log n)となっているので 2 分探索と変わらない。

2 次元

2 次元の場合にも座標圧縮を使用することができる。

座標圧縮 2次元

形を変えずに、圧縮するのでデータ量を減らすことができる。

注意点として座標情報の隣を圧縮時に追加しないと間に存在する空白を削除してしまう可能性がある。

隣の座標を挿入しないでうまくいくケースも存在する。そのため、どういった問題を解決するために使用しているかを考えて、隣の座標を追加するかを考える必要がある。

プログラム

今回題材にした図は両隣に空白として要素を代入する必要があったのであまり圧縮されていないように見えるが、これがもっと大きな図であればかなり圧縮される。

coordinate-compression-2.py
import bisect


def compress(a, b):
vals = []
dx = [-1, 0, 1]
for i in range(len(a)):
for j in dx:
vals.append(a[i] + j)
vals.append(b[i] + j)
return sorted(set(vals))


# X1 Y1 X2 Y2の順で格納
# X1 Y1 左上
# X2 Y2 右下
box = [
(3, 2, 3, 6),
(3, 11, 3, 13),
(7, 6, 7, 8),
(7, 10, 7, 12),
(3, 4, 9, 4),
(8, 11, 13, 11),
]

x1 = []
y1 = []
x2 = []
y2 = []
for i in range(len(box)):
x1.append(box[i][0])
y1.append(box[i][1])
x2.append(box[i][2])
y2.append(box[i][3])

X = compress(x1, x2)
Y = compress(y1, y2)
for i in range(len(x1)):
x1[i] = bisect.bisect_left(X, x1[i])
x2[i] = bisect.bisect_left(X, x2[i])
y1[i] = bisect.bisect_left(Y, y1[i])
y2[i] = bisect.bisect_left(Y, y2[i])

参考