将数字范围映射到另一个


76

数学从来都不是我在学校的强项:(

如何将输入值转换为该范围的相应输出值?

例如,输入值“ 0”将等于输出值“ 500”,输入值“ 254”将等于输出值“ 5500”。如果输入值是50或101,我不知道如何计算输出值。

我敢肯定这很简单,我现在无法思考:)

编辑:我只需要整数,没有分数或任何东西。

Answers:


158

让我们忘记数学,尝试直观地解决这个问题。

首先,如果我们要输入数字范围[地图0x]输出范围[ 0y],我们只是一个适当的量需要的规模。0变为0,x转到y,数字t将转到(y/x)*t

因此,让我们将您的问题简化为上述更简单的问题。

[ input_startinput_end]的输入范围带有input_end - input_start + 1数字。因此,它等效于[ 0r]的范围,其中r = input_end - input_start

同样,输出范围等于[ 0R],其中R = output_end - output_start

输入input等于x = input - input_start。从第一段开始,这将翻译为y = (R/r)*x。然后,我们可以翻译y通过增值回到原来的输出范围output_startoutput = output_start + y

这给我们:

或者,另一种方式:

现在,这是C,C的除法将被截断,您应该尝试通过计算浮点数来获得更准确的答案:

如果想要更正确,则在最后一步中将舍入而不是截断。您可以通过编写一个简单的round函数来做到这一点:

然后:


6
在过去的几十年中,我至少写过几次这个函数,但是这种解释比我自己能提供的更好。+1用于消除鸿沟-每次运行可进行650万次计算。
delrocco

2
这是我在此网站上看到的最好的答案之一。谢谢!
foob​​arbaz

解决问题的绝妙方法!很好回答!
AdeleGoldberg '19

我发现操作顺序在这里不正确。应该是output_start + ((output_end - output_start) * (input - input_start)) / (input_end - input_start) 。看到这个方程式
stacksonstacks

@stacksonstacks对不起,我没有看到区别。你的意思是我应该先相乘然后相除? 至少在数学上(a * b) / c(a / c) * b-相同。为了保证浮点数的准确性,并确保没有溢出,您将需要非常小心操作的顺序,甚至可能根据输入值而具有不同的操作顺序。
Alok Singhal

26

Arduino内置了map

例:

该页面上也有实现:


12
仅供参考,其map功能已损坏。只需将所有参数更改为int即可将其从浮点函数转换,并且分布不完整。
2014年

@Mud仍然,如果我们将所有内容都改回浮点数,则很有用。
Kotauskas

16

公式是

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。这是使问题更容易的简单技术。

我们该怎么做?我们将不得不将所有内容左移input_start量,这样,如果输入x恰好是input_start,它应该给出零。

因此,假设f(x)是执行转换的函数。

让我们尝试一下:

适用于input_start

在这一点上,它尚不可行input_end,因为我们尚未扩展它。

让我们按范围的长度缩小它,然后将最大值(input_end)映射到一个。

好的,让我们尝试一下input_end

F(input_end) = (input_end - input_start) / (input_end - input_start) = 1

太棒了,似乎可以正常工作。

好的,下一步,我们实际上将其缩放到输出范围。它只是乘以输出范围的实际长度是微不足道的,如下所示:

现在,实际上,我们已经快完成了,我们只需要将其向右移动,以使0从output_start开始。

让我们快速尝试一下。

您会看到等式的第一部分几乎乘以零,从而抵消了所有内容,从而使您

让我们也尝试一下input_end

反过来会变成:

如您所见,它现在似乎已正确映射。


12

这里的关键点是在正确的位置进行整数除法(包括取整)。到目前为止,答案都没有正确的括号。这是正确的方法:


3

所要做的就是按比例找出输入的输入范围“有多远”。然后,将该比例应用于输出范围的大小,以绝对值确定输出应在输出范围内的距离。然后,它添加输出范围的开始以获取实际的输出编号。


500除了输入254会产生收益外,这总是给出的5500
Sven Marnach 2011年

2

这保证将ANY范围映射到ANY范围

我写了这种方法,它精确地遵循了将一个数字范围映射到另一个数字范围的代数公式。计算是通过使用double进行的,以保持精度,然后最后,它将返回一个Double,该double具有您在方法参数中指定的许多小数位。

该函数的工作方式如下...注意,我标记了范围A和B的末端-这是因为没有必要使范围从低端开始并从高端结束或类似的事情...您可以将第一个范围设为300到-7000,第二个范围-3500到5500 ...为范围选择的数字没有区别,它将正确地映射它们。

如果您需要返回一个整数,只需告诉它以小数点后一位返回结果,然后将结果转换为int,如下所示:

在OP的原始问题中,他们将使用如下功能:

这是函数:

我什至在计数器中使用了此功能以淡化屏幕上的某些内容,但是由于不透明度是介于0和1之间的值,因此我将计数器设置为从0到100进行计数,然后将范围从0到100映射到0 -1,并且通过将数字从0到100馈给它来获取0到1之间的所有分数...这是一种更干净的方法,因为使用类似这样的函数,主代码看起来比实际执行数学运算更好在飞行中。

您的里程可能会有所不同。:-)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.