अनुपात को बनाए रखते हुए एक संख्या सीमा को दूसरी श्रेणी में बदलें


256

मैं अनुपात को बनाए रखते हुए एक संख्या को दूसरी में बदलने की कोशिश कर रहा हूं। गणित मेरा मजबूत बिंदु नहीं है।

मेरे पास एक छवि फ़ाइल है जहां बिंदु मान -16000.00 से 16000.00 तक हो सकते हैं, हालांकि विशिष्ट सीमा बहुत कम हो सकती है। मैं जो करना चाहता हूं वह इन मानों को पूर्णांक रेंज 0-100 में संपीड़ित करता है, जहां 0 सबसे छोटे बिंदु का मान है, और 100 सबसे बड़ा मूल्य है। बीच में सभी बिंदुओं को एक रिश्तेदार अनुपात रखना चाहिए, भले ही कुछ परिशुद्धता खो रही हो मैं अजगर में ऐसा करना चाहता हूं, लेकिन यहां तक ​​कि एक सामान्य एल्गोरिदम को भी पर्याप्त होना चाहिए। मैं एक एल्गोरिथ्म पसंद करूँगा जहाँ मिनट / अधिकतम या सीमा को समायोजित किया जा सके (यानी, दूसरी श्रेणी 0 से 100 के बजाय -50 से 800 हो सकती है)।


आप दोनों को धन्यवाद, मैं cletus का जवाब दे रहा हूं क्योंकि वह मेरे फॉलोअप का जवाब देने के लिए पहले और एक jerry में गया।
SpliFF

2
वास्तविक क्षमा करें क्लेटस, मैं इसे जेरी को दे रहा हूं क्योंकि वह नया है और उसे अंक चाहिए।
SpliFF

2
अरे यह तो उम्रवाद है! हेहेह, जे / के, कोई चिंता नहीं। :)
क्लेयस

7
यह प्रश्न स्टैकओवरफ्लो प्रश्न क्लोजर ब्रिगेड से कैसे बच गया? ;)
थनिक्कल

जवाबों:


533
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


क्या ओल्डमैक्स के लिए 16000 होना चाहिए या क्या यह पुराने बिंदु सेट में उच्चतम मूल्य हो सकता है (जैसे, 15034.00, उदाहरण के लिए) क्या अंतर महत्वपूर्ण है?
SpliFF

5
आप इसे अपनी इच्छानुसार कुछ भी बना सकते हैं ... ध्यान रखें कि आपको अजीब परिणाम मिल सकते हैं यदि एक सीमा दूसरे के सापेक्ष बहुत छोटी है (बिल्कुल निश्चित नहीं है, लेकिन अगर 1000000 से अधिक कारक अंतर के आकार के बीच अंतर है पर्वतमाला, सुनिश्चित करें कि यह वास्तव में आप की तरह व्यवहार की उम्मीद है ... या फ़्लोटिंग पॉइंट अशुद्धि के बारे में जानें)
jerryjvl

2
इस उत्तर की लोकप्रियता को देखते हुए, अधिक सामान्य मामले के लिए आपको OldMax == OldMin संभावना पर विचार करना चाहिए, जिसके परिणामस्वरूप एक विभाजन शून्य हो सकता है।
उपयोगकर्ता

3
यह कमाल का है। क्या इस रूपांतरण का कोई गणित नाम है?
तारिक

2
इसे रेखीय रूपांतरण कहा जाता है, @ तारिक
रॉड्रिगो बोरबा

65

यह एक सरल रैखिक रूपांतरण है।

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

तो -16000 के पैमाने पर 10000 को 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
ये गलत है। आपको विभाजन से पहले ओल्ड मिन को ओल्ड वैल्यू से घटाना होगा।
SPWorley

20

वास्तव में कुछ मामले हैं जो उपरोक्त उत्तर तोड़ेंगे। जैसे गलत इनपुट वैल्यू, गलत तरीके से इनपुट रेंज, नेगेटिव इनपुट / आउटपुट रेंज।

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

9

एक शर्त है, जब आपके द्वारा जाँच किए जा रहे सभी मान समान हैं, जहाँ @ jerryjvl का कोड NaN लौटाएगा।

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

5

मैंने इसके लिए BNF को नहीं पचाया , लेकिन Arduino प्रलेखन में फ़ंक्शन का एक बड़ा उदाहरण था और यह टूटना है। मैं इसे पायथन में रीमैप (कारण मानचित्र एक अंतर्निर्मित है) को जोड़कर और प्रकार के कास्ट्स और घुंघराले ब्रेस को हटाकर (बस सभी 'लंबे समय को हटाने के लिए) का उपयोग करने में सक्षम था।

मूल

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


2

पेंग्विनटीडी द्वारा प्रदान की गई लिस्टिंग में, मुझे समझ नहीं आया कि रेंज को उल्टा क्यों किया जाता है, यह रेंज को उलट किए बिना काम करता है। रैखिक श्रेणी रूपांतरण रेखीय समीकरण पर आधारित है Y=Xm+n, जहां mऔर nदिए गए पर्वतमाला से प्राप्त होते हैं। श्रेणियों के रूप में संदर्भित करने के बजाय minऔर max, उन्हें 1 और 2 के रूप में संदर्भित करना बेहतर होगा। इसलिए सूत्र यह होगा:

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

Y=y1कब X=x1, कहां और Y=y2कब X=x2x1, 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


धन्यवाद! यहाँ C # रूपांतरण है: float RangeConv(float input, float x1, float x2, float y1, float y2) { return (((input - x1) * (y2 - y1)) / (x2 - x1)) + y1; }
जुनेर

2

आपकी कॉपी और पेस्ट की आसानी के लिए कुछ छोटे पायथन कार्य यहां दिए गए हैं, जिसमें संपूर्ण सूची को स्केल करने के लिए एक फ़ंक्शन भी शामिल है।

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]


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 ++ वेरिएंट

मुझे पेंग्विनट्यूड का सॉल्यूशन उपयोगी लगा, इसलिए मैंने इसे सी ++ में पोर्ट कराया, अगर किसी को इसकी आवश्यकता है:

फ्लोट रीमैप (फ्लोट एक्स, फ्लोट ओमिन, फ्लोट ओमैक्स, फ्लोट एनमिन, फ्लोट एनएमएक्स)

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

1

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

यहां एक जावास्क्रिप्ट संस्करण है जो एक फ़ंक्शन देता है जो पूर्वनिर्धारित स्रोत और गंतव्य श्रेणियों के लिए rescaling करता है, प्रत्येक बार किए जाने वाले गणना की मात्रा को कम करता है।

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

0

शॉर्ट-कट / सरलीकृत प्रस्ताव

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

वेन


1
NewRange/OldRangeयहाँ क्या है ?
जुनेर

0

मैं व्यक्तिगत रूप से सहायक वर्ग का उपयोग करता हूं जो जेनरिक का समर्थन करता है (स्विफ्ट 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) )
    }
}

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

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

0

जावा संस्करण

हमेशा काम करता है कोई बात नहीं तुम इसे खिलाओ!

मैंने अपना सब कुछ विस्तारित कर दिया, ताकि सीखने में आसानी हो। निश्चित रूप से, अंत में गोलाई वैकल्पिक है।

    private long remap(long p, long Amin, long Amax, long Bmin, long Bmax ) {

    double deltaA = Amax - Amin;
    double deltaB = Bmax - Bmin;
    double scale  = deltaB / deltaA;
    double negA   = -1 * Amin;
    double offset = (negA * scale) + Bmin;
    double q      = (p * scale) + offset;
    return Math.round(q);

}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.