web-dev-qa-db-ja.com

数値範囲を別の範囲にマッピングする

数学は学校で私の強いスーツではなかった:(

int input_start = 0; // The lowest number of the range input.
int input_end = 254; // The lowest number of the range input.
int output_start = 500; // The lowest number of the range output.
int output_end = 5500; // The largest number of the range ouput.

int input = 127; // Input value.
int output = 0;

入力値をその範囲の対応する出力値に変換するにはどうすればよいですか?

たとえば、「0」の入力値は「500」の出力値と等しくなり、「254」の入力値は「5500」の出力値と等しくなります。入力値が50または101の場合、出力値を計算する方法がわかりません。

私はそれが簡単だと確信しています、私は今考えることはできません:)

編集:私は整数だけが必要で、分数も何も必要ありません。

55
Joe

数学を忘れて、これを直観的に解決してみましょう。

最初に、範囲[_0_、x]の入力番号を出力範囲[_0_、y]にマッピングする場合、スケールする必要があります。適切な量​​。 0は0に、xyに、数字tは_(y/x)*t_になります。

したがって、問題を上記のより単純な問題に減らしましょう。

[_input_start_、_input_end_]の入力範囲には_input_end - input_start + 1_番号があります。したがって、これは[_0_、r]の範囲と同等です。ここで_r = input_end - input_start_。

同様に、出力範囲は[_0_、R]と同等です。ここで_R = output_end - output_start_です。

inputの入力は_x = input - input_start_と同等です。これは、最初の段落からy = (R/r)*xに変換されます。次に、_output_start_:_output = output_start + y_を追加して、y値を元の出力範囲に変換します。

これにより、次のことができます。

_output = output_start + ((output_end - output_start) / (input_end - input_start)) * (input - input_start)
_

または、別の方法:

_/* Note, "slope" below is a constant for given numbers, so if you are calculating
   a lot of output values, it makes sense to calculate it once.  It also makes
   understanding the code easier */
slope = (output_end - output_start) / (input_end - input_start)
output = output_start + slope * (input - input_start)
_

さて、これはCであり、Cの除算は切り捨てられるので、浮動小数点で計算することにより、より正確な答えを取得しようとする必要があります。

_double slope = 1.0 * (output_end - output_start) / (input_end - input_start)
output = output_start + slope * (input - input_start)
_

さらに正確にしたい場合は、最終ステップで切り捨ての代わりに丸めを行います。これを行うには、単純なround関数を作成します。

_#include <math.h>
double round(double d)
{
    return floor(d + 0.5);
}
_

次に:

_output = output_start + round(slope * (input - input_start))
_
97
Alok Singhal

Arduinoには、この組み込みの map があります。

例:

/* Map an analog value to 8 bits (0 to 255) */
void setup() {}

void loop()
{
  int val = analogRead(0);
  val = map(val, 0, 1023, 0, 255);
  analogWrite(9, val);
}

また、そのページに実装があります。

long map(long x, long in_min, long in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
22
Dustin

ここでの重要なポイントは、適切な場所で整数の除算(丸めを含む)を行うことです。これまでのところ、どの括弧も正しくありませんでした。これが正しい方法です。

int input_range = input_end - input_start;
int output_range = output_end - output_start;

output = (input - input_start)*output_range / input_range + output_start;
11
Sven Marnach

式は

f(x)=(x-input_start)/(input_end-input_start)*(output_end-output_start)+ output_start

私はこの投稿をここに接続します: https://betterexplained.com/articles/rethinking-arithmetic-a-visual-guide/ これを直感的に考え出そうとするのにとても役立ちました。投稿が何を言っているかを理解したら、自分でこれらの公式を思いつくのは簡単です。私もそのような質問に苦労していたことに注意してください。 (所属はありません-非常に便利だとわかりました)

範囲が_[input_start..input_end]_であるとすると、0が_input_start_で、1が_input_end_になるように正規化することから始めましょう。これは、問題を簡単にするための簡単なテクニックです。

どうやってやるの?入力xがたまたま_input_start_である場合、ゼロを与えるように、input_startの量だけ左にすべてをシフトする必要があります。

したがって、f(x)が変換を行う関数であるとしましょう。

_f(x) = x - input_start
_

試してみよう:

_f(input_start) = input_start - input_start = 0
_

_input_start_で機能します。

この時点では、_input_end_に対してはまだ機能しません。スケーリングしていないためです。

範囲の長さだけ縮小し、最大値(input_end)を1つにマッピングします。

_f(x) = (x - input_start) / (input_end - input_start)
_

oK、_input_end_で試してみましょう。

f _(input_end) = (input_end - input_start) / (input_end - input_start) = 1_

素晴らしい、動作するようです。

さて、次のステップでは、実際に出力範囲に合わせてスケーリングします。以下のように、出力範囲の実際の長さを単に乗算するのと同じくらい簡単です。

_f(x) = (x - input_start) / (input_end - input_start) * (output_end - output_start)
_

これで、実際にはほぼ完了しました。0がoutput_startから始まるように、右にシフトする必要があります。

_f(x) = (x - input_start) / (input_end - input_start) * (output_end - output_start) + output_start
_

簡単に試してみましょう。

_f(input_start) = (input_start - input_start) / (input_end - input_start) * (output_end - output_start) + output_start
_

方程式の最初の部分はほとんどゼロで乗算されているため、すべてがキャンセルされ、

_f(input_start) = output_start
_

_input_end_も試してみましょう。

_f(input_end) = (input_end - input_start) / (input_end - input_start) * (output_end - output_start) + output_start
_

次のようになります:

_f(input_end) = output_end - output_start + output_start = output_end
_

ご覧のとおり、正しくマップされているようです。

10
output = ((input - input_start)/(input_end - input_start)) * (output_end - output_start) + output_start

それがすることは、入力が入力範囲に「どれくらいまで」比例しているかを見つけることです。次に、その比率を出力範囲のサイズに適用して、出力が出力範囲内にあるべき範囲を絶対的に調べます。次に、出力範囲の開始を追加して、実際の出力番号を取得します。

2
QuantumMechanic