みさご解体新書

正規化

概要

ある値が範囲の中でどの地点にあるかを割合で表す計算のことを正規化と呼ぶ。

コード例

function norm(a: number, b: number, v: number): number {
  return (v - a) / (b - a);
}
console.log(norm(0, 20, 10)); // 0.5
console.log(norm(5, 15, 10)); // 0.5
console.log(norm(-10, 10, 0)); // 0.5

console.log(norm(20, 0, 10)); // 0.5
console.log(norm(15, 5, 10)); // 0.5
console.log(norm(10, -10, 0)); // 0.5

a, b, v の 3 つの値を渡して、a ~ b を 0 ~ 1 の範囲と考えたとき、v がどの地点にあるかを計算する。

console.log(norm(0, 100, -50)); // -0.5
console.log(norm(0, 100, 150)); // 1.5

v が a ~ b の範囲に無い値の場合は、返却される値が 0 未満または 1 より大きくなる。

解説/アルゴリズム

5 割や 50% というのは 0.5 のことだが、これは 5 / 10 = 0.550 / 100 = 0.5 のように対象の値を上限値で割ることで 0.0 ~ 1.0 の範囲に変換している。

対象の値 / 範囲の上限値

5 割は 0 ~ 10 割の範囲の中にあるので 5 / 10 、50% は 0 ~ 100% の範囲の中にあるので 5 / 100 という計算をすれば範囲の中で値がどの位置にあるかを計算できる。

対象の値を 40、範囲を 0 ~ 200 とすると、 40 / 200 = 0.2 、つまり 40 という値は 0 ~ 200 の範囲の中で 0.2 (20%) の位置にある値ということが分かる。

ただし、範囲の下限が 0 ではない場合は計算が上記より複雑になる。

たとえば範囲が 20 ~ 100 の場合で、対象の値が 50 の場合、これは範囲の真ん中にある値ではない。

この場合の解決方法として、下限値を 0 にするように範囲の幅をずらす方法がある。

20 ~ 100 の範囲の場合、下限/上限とも下限の値である 20 を引いて、0 ~ 80 の範囲に変換する。

対象の値も同じように 20 を引いて50-20=30に変換する。

それぞれの値自体は計算によって変わりはしたが、同じ値を引いているので対象の値が範囲の中でのポジションが変わることはない。

あとは先程の手順通り、対象の値を上限値で割るだけで正規化できる。

(対象の値 - 下限値) / (上限値 - 下限値)

対象の値が 50、範囲が 20 ~ 100 なので、(50 - 20) / (100 - 20) = 0.375で、20 ~ 100 の範囲で 50 は 37.5%の位置にあるということがわかる。