如何用已知的最小值和最大值来缩小一系列数字
所以我想弄清楚如何取一个数字的范围,并把这个数值缩小到一个范围。 想要这样做的原因是,我正在试图在java swing jpanel中绘制椭圆。 我想要每个椭圆的高度和宽度在1-30的范围内。 我有从我的数据集中find最小值和最大值的方法,但是直到运行时才会有最小值和最大值。 是否有捷径可寻?
假设您想将范围[min,max]
缩放到[a,b]
。 你正在寻找一个满足的(连续的)函数
f(min) = a f(max) = b
在你的情况下, a
会是1, b
会是30,但是让我们从简单的事情开始,尝试将[min,max]
映射到[0,1]
的范围内。
把min
放到一个函数中,然后用0来完成
f(x) = x - min ===> f(min) = min - min = 0
所以这几乎是我们想要的。 但是,当我们真正想要1时,放入max
将会给我们max - min
。所以我们必须调整它:
x - min max - min f(x) = --------- ===> f(min) = 0; f(max) = --------- = 1 max - min max - min
这是我们想要的。 所以我们需要做一个翻译和一个缩放。 现在,如果我们想要得到a
和b
任意值,我们需要一些更复杂的东西:
(ba)(x - min) f(x) = -------------- + a max - min
你可以validation现在给x
inputmin
,并把max
赋给b
。
您可能还会注意到(ba)/(max-min)
是新范围的大小与原始范围的大小之间的比例因子。 所以,我们首先用-min
转换x
, -min
它转换成正确的因子,然后把它转换回a
最小值。
希望这可以帮助。
为了方便起见,这里是以Javaforms的Irritatealgorithm。 添加错误检查,exception处理和必要的调整。
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; } }
testing:
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
这里有一些JavaScript复制粘贴缓解(这是刺激的答案):
function scaleBetween(unscaledNum, minAllowed, maxAllowed, min, max) { return (maxAllowed - minAllowed) * (unscaledNum - min) / (max - min) + minAllowed; }
像这样应用,将范围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)); }
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); }
像这样应用:
[-4, 0, 5, 6, 9].scaleBetween(0, 100);
[0,30.76923076923077,69.23076923076923,76.92307692307692,100]
我遇到了这个解决scheme,但这并不适合我的需要。 所以我在d3源代码中挖了一下。 我个人会build议像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)); } }
这里是testing,你可以看到我的意思
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); } }