تحويل عدد كبير إلى نطاق آخر، والحفاظ على نسبة

StackOverflow https://stackoverflow.com/questions/929103

  •  06-09-2019
  •  | 
  •  

سؤال

أحاول تحويل مجموعة واحدة من الأرقام إلى آخر، والحفاظ على نسبة. الرياضيات ليست وجهة نظري القوية.

لدي ملف صورة حيث قد تتراوح قيم النقطة من -16000.00 إلى 16000.00 على الرغم من أن النطاق النموذجي قد يكون أقل بكثير. ما أريد القيام به هو ضغط هذه القيم في نطاق عدد صحيح 0-100، حيث 0 هي قيمة أصغر النقطة، و 100 هي قيمة الأكبر. يجب أن تبقي جميع النقاط بين ما بينها نسبة نسبية على الرغم من أن بعض الدقة تضيع أود القيام بذلك في بيثون ولكن حتى خوارزمية عامة يجب أن تكفي. أفضل خوارزمية حيث يمكن تعديل الحد الأدنى / الحد الأقصى أو أي نطاق (أي، يمكن أن يكون النطاق الثاني -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

نصائح أخرى

هذا تحويل خطي بسيط.

new_value = ( (old_value - old_min) / (old_max - old_min) ) * (new_max - new_min) + new_min

لتحويل 10000 على مقياس -16000 إلى 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

في الواقع هناك بعض الحالات التي من شأنها أن تنكسر أعلاه. مثل قيمة الإدخال بشكل خاطئ، نطاق الإدخال بشكل خاطئ، نطاقات الإدخال / الإخراج السلبي.

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

هناك شرط، عندما تكون كل القيم التي تتحقق منها هي نفسها، حيث سيعود رمز jerryjvl نان.

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

أنا لم أحفر BNF. لهذا، لكن وثائق اردوينو لديها مثال رائع على الوظيفة وهو انهيار. كنت قادرا على استخدام هذا في Python ببساطة عن طريق إضافة إعادة تسمية REPAP إلى REMAP (السبب "خريطة مدمجة) وإزالة المصابيح الناشئة والأقواس المجعد (أي ما عليك سوى إزالة كل" Long's) ".

إبداعي

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

بيثون

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.

في القائمة المقدمة من 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.

PHP Port.

العثور على حل 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;
}

لقد استخدمت هذا الحل في مشكلة كنت حل في 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 ++ إذا احتاج أي شخص إلى ذلك:

تعويم REMAP (تعويم X، تعويم العمين، تعويم Omax، تعويم NMIN، تعويم NMAX) {

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

فيما يلي بعض وظائف 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]

مقترح قصير / مقترح مبسط

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

وين

أنا شخصيا استخدم فئة المساعد التي تدعم الأجداد (سويفت 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) )
    }
}

يحول هذا المثال أغاني الموضع الحالي إلى حد زاوية من 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;
    }

فيما يلي إصدار JavaScript الذي يقوم بإرجاع وظيفة تقوم بإعادة الإنتقال إلى المصدر المحدد مسبقا والنطاقات الوجهة، مما يقلل من مقدار الحساب الذي يجب القيام به في كل مرة.

// This function returns a function bound to the 
// min/max source & target ranges given.
// oMin, oMax = source
// nMin, nMax = dest.
function makeRangeMapper(oMin, oMax, nMin, nMax ){
    //range check
    if (oMin == oMax){
        console.log("Warning: Zero input range");
        return undefined;
    };

    if (nMin == nMax){
        console.log("Warning: Zero output range");
        return undefined
    }

    //check reversed input range
    var reverseInput = false;
    let oldMin = Math.min( oMin, oMax );
    let oldMax = Math.max( oMin, oMax );
    if (oldMin != oMin){
        reverseInput = true;
    }

    //check reversed output range
    var reverseOutput = false;  
    let newMin = Math.min( nMin, nMax )
    let newMax = Math.max( nMin, nMax )
    if (newMin != nMin){
        reverseOutput = true;
    }

    // Hot-rod the most common case.
    if (!reverseInput && !reverseOutput) {
        let dNew = newMax-newMin;
        let dOld = oldMax-oldMin;
        return (x)=>{
            return ((x-oldMin)* dNew / dOld) + newMin;
        }
    }

    return (x)=>{
        let portion;
        if (reverseInput){
            portion = (oldMax-x)*(newMax-newMin)/(oldMax-oldMin);
        } else {
            portion = (x-oldMin)*(newMax-newMin)/(oldMax-oldMin)
        }
        let result;
        if (reverseOutput){
            result = newMax - portion;
        } else {
            result = portion + newMin;
        }

        return result;
    }   
}

فيما يلي مثال على استخدام هذه الوظيفة لتوسيع نطاق 0-1 في -0x80000000، 0x7FFFFFFF

let normTo32Fn = makeRangeMapper(0, 1, -0x80000000, 0x7FFFFFFF);
let fs = normTo32Fn(0.5);
let fs2 = normTo32Fn(0);

قائمة الفهم حل واحد بطانة

color_array_new = [int((((x - min(node_sizes)) * 99) / (max(node_sizes) - min(node_sizes))) + 1) for x in node_sizes]

الإصدار الأطول

def colour_specter(waste_amount):
color_array = []
OldRange = max(waste_amount) - min(waste_amount)
NewRange = 99
for number_value in waste_amount:
    NewValue = int((((number_value - min(waste_amount)) * NewRange) / OldRange) + 1)
    color_array.append(NewValue)
print(color_array)
return color_array
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top