تحويل عدد كبير إلى نطاق آخر، والحفاظ على نسبة
سؤال
أحاول تحويل مجموعة واحدة من الأرقام إلى آخر، والحفاظ على نسبة. الرياضيات ليست وجهة نظري القوية.
لدي ملف صورة حيث قد تتراوح قيم النقطة من -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
في القائمة المقدمة من 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