如何缩小具有已知最小值和最大值的数字范围

use*_*271 220 math scaling range max minimum

因此,我试图找出如何获取一系列数字并将值缩小到适合范围.想要这样做的原因是我试图在java swing jpanel中绘制省略号.我希望每个椭圆的高度和宽度在1-30的范围内.我有从我的数据集中找到最小值和最大值的方法,但是直到运行时才会有min和max.是否有捷径可寻?

irr*_*ate 489

假设您要将范围缩放[min,max][a,b].您正在寻找满足的(连续)功能

f(min) = a
f(max) = b
Run Code Online (Sandbox Code Playgroud)

在你的情况下,a将是1,b将是30,但让我们从更简单的东西开始,并尝试映射[min,max]到范围[0,1].

min成函数获得了0可以与实现

f(x) = x - min   ===>   f(min) = min - min = 0
Run Code Online (Sandbox Code Playgroud)

这几乎就是我们想要的.但是当我们真正想要时,投入max会给我们max - min1.所以我们必须扩展它:

        x - min                                  max - min
f(x) = ---------   ===>   f(min) = 0;  f(max) =  --------- = 1
       max - min                                 max - min
Run Code Online (Sandbox Code Playgroud)

这就是我们想要的.所以我们需要进行翻译和缩放.现在,如果不是我们想要得到的任意值ab,我们需要一些更复杂的:

       (b-a)(x - min)
f(x) = --------------  + a
          max - min
Run Code Online (Sandbox Code Playgroud)

您可以验证投入minx现在给a,并把在maxb.

您可能还会注意到,这(b-a)/(max-min)是新范围的大小和原始范围的大小之间的缩放系数.因此,其实我们是第一平移x通过-min,它扩展到了正确的因素,然后再翻译回最多的新的最小值a.

希望这可以帮助.

  • 这是否确保我重新调整的变量保留原始分发? (10认同)
  • 只是提醒一下:使用`max!= min`模型会更准确,否则函数结果不确定:) (4认同)
  • 这是线性比例的一个很好的实现.这可以很容易地转变为logarighmic尺度? (2认同)
  • @Andrew `min` 和 `max` 可以是正值也可以是负值。一般来说,在数学中,如果需要的话,我们会为变量声明一个条件。如果没有条件,就像在本例中一样,我们假设 min 和 max 是_任意_数字。对于线性变换,值是 +ve 还是 -ve 并不重要(想象一下 ay = mx+c 曲线,无论 x > 0 还是 x < 0,它看起来都是一样的)。 (2认同)

Cha*_*ton 43

这里有一些用于复制粘贴的JavaScript(这是烦人的答案):

function scaleBetween(unscaledNum, minAllowed, maxAllowed, min, max) {
  return (maxAllowed - minAllowed) * (unscaledNum - min) / (max - min) + minAllowed;
}
Run Code Online (Sandbox Code Playgroud)

像这样应用,将范围10-50缩放到0-100之间的范围.

var unscaledNums = [10, 13, 25, 28, 43, 50];

var maxRange = Math.max.apply(Math, unscaledNums);
var minRange = Math.min.apply(Math, unscaledNums);

for (var i = 0; i < unscaledNums.length; i++) {
  var unscaled = unscaledNums[i];
  var scaled = scaleBetween(unscaled, 0, 100, minRange, maxRange);
  console.log(scaled.toFixed(2));
}
Run Code Online (Sandbox Code Playgroud)

0.00,18.37,48.98,55.10,85.71,100.00

编辑:

我知道我很久以前就回答了这个问题,但是我现在使用的是一个更清洁的功能:

Array.prototype.scaleBetween = function(scaledMin, scaledMax) {
  var max = Math.max.apply(Math, this);
  var min = Math.min.apply(Math, this);
  return this.map(num => (scaledMax-scaledMin)*(num-min)/(max-min)+scaledMin);
}
Run Code Online (Sandbox Code Playgroud)

应用如下:

[-4, 0, 5, 6, 9].scaleBetween(0, 100);
Run Code Online (Sandbox Code Playgroud)

[0,30.76923076923077,69.23076923076923,76.92307692307692,100]

  • 这是一种边缘情况,但如果数组仅包含一个值或仅包含同一值的多个副本,这种情况就会消失。因此 [1].scaleBetween(1, 100) 和 [1,1,1].scaleBetween(1,100) 都用 NaN 填充输出。 (2认同)

Jav*_*a42 27

为方便起见,这里是Java形式的Irritate算法.根据需要添加错误检查,异常处理和调整.

public class Algorithms { 
    public static double scale(final double valueIn, final double baseMin, final double baseMax, final double limitMin, final double limitMax) {
        return ((limitMax - limitMin) * (valueIn - baseMin) / (baseMax - baseMin)) + limitMin;
    }
}
Run Code Online (Sandbox Code Playgroud)

测试:

final double baseMin = 0.0;
final double baseMax = 360.0;
final double limitMin = 90.0;
final double limitMax = 270.0;
double valueIn = 0;
System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax));
valueIn = 360;
System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax));
valueIn = 180;
System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax));

90.0
270.0
180.0
Run Code Online (Sandbox Code Playgroud)


Vic*_*Vic 20

这是我理解的方式:


x一个范围内的百分比是多少

假设你有一个范围从.0100.给定该范围内的任意数字,该范围内的"百分比"是多少?这应该是相当简单的,00%,5050%100100%.

现在,如果你的范围是20100?我们不能应用与上面相同的逻辑(除以100),因为:

20 / 100
Run Code Online (Sandbox Code Playgroud)

不给我们0(20应该是0%现在).这应该很容易修复,我们只需要0为该情况制作分子20.我们可以通过减去:

(20 - 20) / 100
Run Code Online (Sandbox Code Playgroud)

但是,这不再适用100了,因为:

(100 - 20) / 100
Run Code Online (Sandbox Code Playgroud)

不给我们100%.同样,我们也可以通过从分母中减去它来解决这个问题:

(100 - 20) / (100 - 20)
Run Code Online (Sandbox Code Playgroud)

找出%x在一个范围内的更通用的等式将是:

(x - MIN) / (MAX - MIN)
Run Code Online (Sandbox Code Playgroud)

缩放范围到另一个范围

现在我们知道数字在一个范围内的百分比,我们可以应用它来将数字映射到另一个范围.我们来看一个例子吧.

old range = [200, 1000]
new range = [10, 20]
Run Code Online (Sandbox Code Playgroud)

如果我们在旧范围内有一个数字,那么这个数字在新范围内会是多少?让我们说数字是400.首先,弄清楚400旧范围内的百分比.我们可以应用上面的等式.

(400 - 200) / (1000 - 200) = 0.25
Run Code Online (Sandbox Code Playgroud)

所以,400在于25%旧范围.我们只需要确定25%新范围的数量.想想什么50%[0, 20]是.这样10对吗?你是怎么得到答案的?好吧,我们可以这样做:

20 * 0.5 = 10
Run Code Online (Sandbox Code Playgroud)

但是,怎么样[10, 20]?我们现在需要改变一切10.例如:

((20 - 10) * 0.5) + 10
Run Code Online (Sandbox Code Playgroud)

一个更通用的公式是:

((MAX - MIN) * PERCENT) + MIN
Run Code Online (Sandbox Code Playgroud)

要什么样的原始示例25%[10, 20]是:

((20 - 10) * 0.25) + 10 = 12.5
Run Code Online (Sandbox Code Playgroud)

因此,400在范围内[200, 1000]将映射到12.5范围内[10, 20]


TLDR

要从x旧范围映射到新范围:

OLD PERCENT = (x - OLD MIN) / (OLD MAX - OLD MIN)
NEW X = ((NEW MAX - NEW MIN) * OLD PERCENT) + NEW MIN
Run Code Online (Sandbox Code Playgroud)

  • 我就是这么解决的。最棘手的部分是找出数字在给定范围内的比率。它应该始终在 [0, 1] 范围内,就像百分比一样,例如 0.5 代表 50%。接下来,您只需扩展/拉伸和移动该数字以适合您所需的范围。 (2认同)

KIC*_*KIC 11

我遇到了这个解决方案,但这并不适合我的需要.所以我在d3源代码中挖掘了一下.我个人会建议像d3.scale那样做.

所以在这里你将域缩放到范围.优点是您可以将标志翻转到目标范围.这很有用,因为计算机屏幕上的y轴自上而下,因此较大的值具有较小的y.

public class Rescale {
    private final double range0,range1,domain0,domain1;

    public Rescale(double domain0, double domain1, double range0, double range1) {
        this.range0 = range0;
        this.range1 = range1;
        this.domain0 = domain0;
        this.domain1 = domain1;
    }

    private double interpolate(double x) {
        return range0 * (1 - x) + range1 * x;
    }

    private double uninterpolate(double x) {
        double b = (domain1 - domain0) != 0 ? domain1 - domain0 : 1 / domain1;
        return (x - domain0) / b;
    }

    public double rescale(double x) {
        return interpolate(uninterpolate(x));
    }
}
Run Code Online (Sandbox Code Playgroud)

这是测试,你可以看到我的意思

public class RescaleTest {

    @Test
    public void testRescale() {
        Rescale r;
        r = new Rescale(5,7,0,1);
        Assert.assertTrue(r.rescale(5) == 0);
        Assert.assertTrue(r.rescale(6) == 0.5);
        Assert.assertTrue(r.rescale(7) == 1);

        r = new Rescale(5,7,1,0);
        Assert.assertTrue(r.rescale(5) == 1);
        Assert.assertTrue(r.rescale(6) == 0.5);
        Assert.assertTrue(r.rescale(7) == 0);

        r = new Rescale(-3,3,0,1);
        Assert.assertTrue(r.rescale(-3) == 0);
        Assert.assertTrue(r.rescale(0) == 0.5);
        Assert.assertTrue(r.rescale(3) == 1);

        r = new Rescale(-3,3,-1,1);
        Assert.assertTrue(r.rescale(-3) == -1);
        Assert.assertTrue(r.rescale(0) == 0);
        Assert.assertTrue(r.rescale(3) == 1);
    }
}
Run Code Online (Sandbox Code Playgroud)