将数字范围转换为另一个范围,保持比例
我试图将一个数字范围转换为另一个范围,保持比例。数学不是我的强项。将数字范围转换为另一个范围,保持比例
我有一个图像文件,其中点值可能范围从-16000.00到16000.00,虽然典型的范围可能会少得多。我想要做的是将这些值压缩到0-100的整数范围内,其中0是最小点的值,100是最大值。所有的点之间应该保持一个相对比例,即使有一些精度正在丧失我想在python中做到这一点,但即使是一般算法也应该足够。我更喜欢一种算法,其中可以调整最小/最大或任一范围(即,第二范围可以是-50至800而不是0至100)。
NewValue = (((OldValue - OldMin) * (NewMax - NewMin))/(OldMax - OldMin)) + NewMin
或者多一点可读:
OldRange = (OldMax - OldMin)
NewRange = (NewMax - NewMin)
NewValue = (((OldValue - OldMin) * NewRange)/OldRange) + NewMin
或者如果您要保护旧范围为0的情况(OldMin = OldMax):
OldRange = (OldMax - OldMin)
if (OldRange == 0)
NewValue = NewMin
else
{
NewRange = (NewMax - NewMin)
NewValue = (((OldValue - OldMin) * NewRange)/OldRange) + NewMin
}
请注意,在这种情况下,我们不得不任意选择一个可能的新范围值。根据上下文,明智的选择可能是:NewMin
(看样)NewMax
或(NewMin + NewMax)/2
oldMax必须是16000还是可以是旧点集中的最高值(例如,15034.00)是否区别重要? – SpliFF 2009-05-30 06:30:40
这是一个简单的线性转换。
new_value = ((old_value - old_min)/(old_max - old_min)) * (new_max - new_min) + new_min
等等-16000规模转换为10000〜16000至0新分至100个产量:
old_value = 10000
old_min = -16000
old_max = 16000
new_min = 0
new_max = 100
new_value = ((10000 - -16000)/(16000 - -16000)) * (100 - 0) + 0
= 81.25
有一个条件,当所有你正在检查的值是相同的,其中@ jerryjvl的代码将返回NaN的。
if (OldMin != OldMax && NewMin != NewMax):
return (((OldValue - OldMin) * (NewMax - NewMin))/(OldMax - OldMin)) + NewMin
else:
return (NewMax + NewMin)/2
实际上有些情况下,上面的答案会打破。 如错误输入值,错误输入范围,负输入/输出范围。
def remap(x, oMin, oMax, nMin, nMax):
#range check
if oMin == oMax:
print "Warning: Zero input range"
return None
if nMin == nMax:
print "Warning: Zero output range"
return None
#check reversed input range
reverseInput = False
oldMin = min(oMin, oMax)
oldMax = max(oMin, oMax)
if not oldMin == oMin:
reverseInput = True
#check reversed output range
reverseOutput = False
newMin = min(nMin, nMax)
newMax = max(nMin, nMax)
if not newMin == nMin :
reverseOutput = True
portion = (x-oldMin)*(newMax-newMin)/(oldMax-oldMin)
if reverseInput:
portion = (oldMax-x)*(newMax-newMin)/(oldMax-oldMin)
result = portion + newMin
if reverseOutput:
result = newMax - portion
return result
#test cases
print remap(25.0, 0.0, 100.0, 1.0, -1.0), "==", 0.5
print remap(25.0, 100.0, -100.0, -1.0, 1.0), "==", -0.25
print remap(-125.0, -100.0, -200.0, 1.0, -1.0), "==", 0.5
print remap(-125.0, -200.0, -100.0, -1.0, 1.0), "==", 0.5
#even when value is out of bound
print remap(-20.0, 0.0, 100.0, 0.0, 1.0), "==", -0.2
我在js中解决的问题中使用了这个解决方案,所以我想我会分享翻译。感谢您的解释和解决方案。
function remap(x, oMin, oMax, nMin, nMax){
//range check
if (oMin == oMax){
console.log("Warning: Zero input range");
return None;
};
if (nMin == nMax){
console.log("Warning: Zero output range");
return None
}
//check reversed input range
var reverseInput = false;
oldMin = Math.min(oMin, oMax);
oldMax = Math.max(oMin, oMax);
if (oldMin != oMin){
reverseInput = true;
}
//check reversed output range
var reverseOutput = false;
newMin = Math.min(nMin, nMax)
newMax = Math.max(nMin, nMax)
if (newMin != nMin){
reverseOutput = true;
};
var portion = (x-oldMin)*(newMax-newMin)/(oldMax-oldMin)
if (reverseInput){
portion = (oldMax-x)*(newMax-newMin)/(oldMax-oldMin);
};
var result = portion + newMin
if (reverseOutput){
result = newMax - portion;
}
return result;
}
C++变
我发现PenguinTD的解决方案有用的,所以我把它移植到C++,如果有人需要它:
浮重映射(浮法X,浮法OMIN,浮OMAX,浮无机氮,浮n最大){
//range check if(oMin == oMax) { //std::cout<< "Warning: Zero input range"; return -1; } if(nMin == nMax){ //std::cout<<"Warning: Zero output range"; return -1; } //check reversed input range bool reverseInput = false; float oldMin = min(oMin, oMax); float oldMax = max(oMin, oMax); if (oldMin == oMin) reverseInput = true; //check reversed output range bool reverseOutput = false; float newMin = min(nMin, nMax); float newMax = max(nMin, nMax); if (newMin == nMin) reverseOutput = true; float portion = (x-oldMin)*(newMax-newMin)/(oldMax-oldMin); if (reverseInput) portion = (oldMax-x)*(newMax-newMin)/(oldMax-oldMin); float result = portion + newMin; if (reverseOutput) result = newMax - portion; return result; }
在由PenguinTD提供的房源,我不取消了解为什么范围是相反的,它的工作原理没有必要颠倒范围。线性范围转换基于线性方程Y=Xm+n
,其中m
和n
是从给定的范围导出的。而不是将范围称为min
和max
,最好将它们称为1和2。因此,该公式将是:
Y = (((X - x1) * (y2 - y1))/(x2 - x1)) + y1
凡Y=y1
时X=x1
,并Y=y2
时X=x2
。 x1
,x2
,y1
& y2
可以给出任何值positive
或negative
的值。在宏中定义表达式使其更有用,它可以用于任何参数名称。
#define RangeConv(X, x1, x2, y1, y2) (((float)((X - x1) * (y2 - y1))/(x2 - x1)) + y1)
的float
投将确保浮点除法的情况下所有的参数都是integer
值。 根据不同的应用,可能不需要检查范围x1=x2
和y1==y2
。
短切/简体建议
NewRange/OldRange = Handy multiplicand or HM
Convert OldValue in OldRange to NewValue in NewRange =
(OldValue - OldMin x HM) + NewMin
韦恩
PHP端口
找到PenguinTD的解决方案有帮助的,所以我把它移植到PHP。帮助你自己!
/**
* =====================================
* Remap Range
* =====================================
* - Convert one range to another. (including value)
*
* @param int $intValue The value in the old range you wish to convert
* @param int $oMin The minimum of the old range
* @param int $oMax The maximum of the old range
* @param int $nMin The minimum of the new range
* @param int $nMax The maximum of the new range
*
* @return float $fResult The old value converted to the new range
*/
function remapRange($intValue, $oMin, $oMax, $nMin, $nMax) {
// Range check
if ($oMin == $oMax) {
echo 'Warning: Zero input range';
return false;
}
if ($nMin == $nMax) {
echo 'Warning: Zero output range';
return false;
}
// Check reversed input range
$bReverseInput = false;
$intOldMin = min($oMin, $oMax);
$intOldMax = max($oMin, $oMax);
if ($intOldMin != $oMin) {
$bReverseInput = true;
}
// Check reversed output range
$bReverseOutput = false;
$intNewMin = min($nMin, $nMax);
$intNewMax = max($nMin, $nMax);
if ($intNewMin != $nMin) {
$bReverseOutput = true;
}
$fRatio = ($intValue - $intOldMin) * ($intNewMax - $intNewMin)/($intOldMax - $intOldMin);
if ($bReverseInput) {
$fRatio = ($intOldMax - $intValue) * ($intNewMax - $intNewMin)/($intOldMax - $intOldMin);
}
$fResult = $fRatio + $intNewMin;
if ($bReverseOutput) {
$fResult = $intNewMax - $fRatio;
}
return $fResult;
}
下面是一些简短的Python函数,用于复制和粘贴,包括缩放整个列表的函数。
def scale_number(unscaled, to_min, to_max, from_min, from_max):
return (to_max-to_min)*(unscaled-from_min)/(from_max-from_min)+to_min
def scale_list(l, to_min, to_max):
return [scale_number(i, to_min, to_max, min(l), max(l)) for i in l]
哪位能像这样被使用:
scale_list([1,3,4,5], 0, 100)
[0.0,50.0,75.0,100.0]
在我的情况下,我想是按比例的对数曲线,像所以:
scale_list([math.log(i+1) for i in range(5)], 0, 50)
[0.0,21.533827903669653,34.130309724299266,43.06765580733931,50.0]
我个人使用支持泛型(SWIFT 3兼容)
struct Rescale<Type : BinaryFloatingPoint> {
typealias RescaleDomain = (lowerBound: Type, upperBound: Type)
var fromDomain: RescaleDomain
var toDomain: RescaleDomain
init(from: RescaleDomain, to: RescaleDomain) {
self.fromDomain = from
self.toDomain = to
}
func interpolate(_ x: Type) -> Type {
return self.toDomain.lowerBound * (1 - x) + self.toDomain.upperBound * x;
}
func uninterpolate(_ x: Type) -> Type {
let b = (self.fromDomain.upperBound - self.fromDomain.lowerBound) != 0 ? self.fromDomain.upperBound - self.fromDomain.lowerBound : 1/self.fromDomain.upperBound;
return (x - self.fromDomain.lowerBound)/b
}
func rescale(_ x: Type) -> Type {
return interpolate(uninterpolate(x))
}
}
没挖掉BNF该助手类,但Arduino文档有一个很好的例子,它的功能和故障。我可以通过简单地添加一个def重命名来重新映射(因为map是一个内置的)并删除类型转换和花括号(即只删除所有'long')来在Python中使用它。
原始
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;
}
的Python
def remap(x, in_min, in_max, out_min, out_max):
return (x - in_min) * (out_max - out_min)/(in_max - in_min) + out_min
此示例的歌曲的当前位置转换为20的角度范围 - 40
/// <summary>
/// This test converts Current songtime to an angle in a range.
/// </summary>
[Fact]
public void ConvertRangeTests()
{
//Convert a songs time to an angle of a range 20 - 40
var result = ConvertAndGetCurrentValueOfRange(
TimeSpan.Zero, TimeSpan.FromMinutes(5.4),
20, 40,
2.7
);
Assert.True(result == 30);
}
/// <summary>
/// Gets the current value from the mixValue maxValue range.
/// </summary>
/// <param name="startTime">Start of the song</param>
/// <param name="duration"></param>
/// <param name="minValue"></param>
/// <param name="maxValue"></param>
/// <param name="value">Current time</param>
/// <returns></returns>
public double ConvertAndGetCurrentValueOfRange(
TimeSpan startTime,
TimeSpan duration,
double minValue,
double maxValue,
double value)
{
var timeRange = duration - startTime;
var newRange = maxValue - minValue;
var ratio = newRange/timeRange.TotalMinutes;
var newValue = value * ratio;
var currentValue= newValue + minValue;
return currentValue;
}
谢谢两位,我得到安宁g克莱图斯的答案,因为他首先得到了答案,还有一个给杰里+1的答案,以回答我的后续问题。 – SpliFF 2009-05-30 07:35:21
实际抱歉cletus,我把它交给杰瑞,因为他是新的,需要点。 – SpliFF 2009-05-30 07:36:55
嘿,这是年龄歧视! heheh,j/k,不用担心。 :) – cletus 2009-05-30 07:52:21