2009-05-30 111 views
177

我試圖將一個數字範圍轉換爲另一個範圍,保持比例。數學不是我的強項。將數字範圍轉換爲另一個範圍,保持比例

我有一個圖像文件,其中點值可能範圍從-16000.00到16000.00,雖然典型的範圍可能會少得多。我想要做的是將這些值壓縮到0-100的整數範圍內,其中0是最小點的值,100是最大值。所有的點之間應該保持一個相對比例,即使有一些精度正在喪失我想在python中做到這一點,但即使是一般算法也應該足夠。我更喜歡一種算法,其中可以調整最小/最大或任一範圍(即,第二範圍可以是-50至800而不是0至100)。

+0

謝謝兩位,我得到安寧g克萊圖斯的答案,因爲他首先得到了答案,還有一個給傑裏+1的答案,以回答我的後續問題。 – SpliFF 2009-05-30 07:35:21

+1

實際抱歉cletus,我把它交給傑瑞,因爲他是新的,需要點。 – SpliFF 2009-05-30 07:36:55

+1

嘿,這是年齡歧視! heheh,j/k,不用擔心。 :) – cletus 2009-05-30 07:52:21

回答

353
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

+0

oldMax必須是16000還是可以是舊點集中的最高值(例如,15034.00)是否區別重要? – SpliFF 2009-05-30 06:30:40

49

這是一個簡單的線性轉換。

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 
+2

這是錯誤的。你需要在分割之前從Old Value中減去Old Min。 – SPWorley 2009-05-30 05:54:43

+21

嗯,我是...... – cletus 2009-05-30 05:56:13

10

有一個條件,當所有你正在檢查的值是相同的,其中@ jerryjvl的代碼將返回NaN的。

if (OldMin != OldMax && NewMin != NewMax): 
    return (((OldValue - OldMin) * (NewMax - NewMin))/(OldMax - OldMin)) + NewMin 
else: 
    return (NewMax + NewMin)/2 
19

實際上有些情況下,上面的答案會打破。 如錯誤輸入值,錯誤輸入範圍,負輸入/輸出範圍。

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 
1

我在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; 
} 
1

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; } 
2

在由PenguinTD提供的房源,我不取消了解爲什麼範圍是相反的,它的工作原理沒有必要顛倒範圍。線性範圍轉換基於線性方程Y=Xm+n,其中mn是從給定的範圍導出的。而不是將範圍稱爲minmax,最好將它們稱爲1和2。因此,該公式將是:

Y = (((X - x1) * (y2 - y1))/(x2 - x1)) + y1 

Y=y1X=x1,並Y=y2X=x2x1,x2,y1 & y2可以給出任何值positivenegative的值。在宏中定義表達式使其更有用,它可以用於任何參數名稱。

#define RangeConv(X, x1, x2, y1, y2) (((float)((X - x1) * (y2 - y1))/(x2 - x1)) + y1) 

float投將確保浮點除法的情況下所有的參數都是integer值。 根據不同的應用,可能不需要檢查範圍x1=x2y1==y2

0

短切/簡體建議

NewRange/OldRange = Handy multiplicand or HM 
Convert OldValue in OldRange to NewValue in NewRange = 
(OldValue - OldMin x HM) + NewMin 

韋恩

2

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; 
} 
1

下面是一些簡短的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]

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)) 
    } 
} 
2

沒挖掉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 

https://www.arduino.cc/en/reference/map

0

此示例的歌曲的當前位置轉換爲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; 
    } 
相關問題