2008-12-04 3 views
2

У меня есть метод C#, который проектирует значение числа от интервала до целевого интервала.
Например: у нас есть интервал -1000 и 9000 и значение 5000; если мы хотим, чтобы проецировать это значение интервала 0..100 мы получаем 60.Сделать алгоритм C# более эффективным

Вот метод:

/// <summary> 
/// Projects a value to an interval 
/// </summary> 
/// <param name="val">The value that needs to be projected</param> 
/// <param name="min">The minimum of the interval the value comes from</param> 
/// <param name="max">The maximum of the interval the value comes from</param> 
/// <param name="intervalTop">The minimum of the interval the value will 
/// be projected to</param> 
/// <param name="intervalBottom">The maximum of the interval the value will 
/// be projected to</param> 
/// <returns>Projected value</returns> 
public decimal ProjectValueToInterval(decimal val, 
             decimal min, 
             decimal max, 
             decimal intervalBottom, 
             decimal intervalTop) 
{ 
    decimal newMin = Math.Min(0, min); 
    decimal valueIntervalSize = Math.Abs(max - newMin); 
    decimal targetIntervalSize = Math.Abs(intervalTop - intervalBottom); 

    decimal projectionUnit = targetIntervalSize/valueIntervalSize; 

    return (val * projectionUnit) + Math.Abs((newMin * projectionUnit)); 
} 

Этот метод должен быть вызван для тысяч значений.
Мне было интересно, есть ли более эффективный способ сделать это на C#? Если да, какие изменения вы предлагаете?

+0

Почему "decimal newMin = Math.Min (0, min);"? Разве это не дает вам неправильный ответ за любой положительный минимум? – VVS 2008-12-04 10:16:28

+0

Вы правы, Math.Min (0, min) делает значения в диапазоне от 0; Я забыл это вынести, но это не влияет на вопрос ... – Germstorm 2008-12-04 13:27:56

ответ

4

Просто математика.То, что вы «проецируете», является нормировкой диапазонов AB и A'-B 'таких, что:

Отношение r = (xA)/(BA) = (y-A')/(B'-A ')

, который, используя свои термины как:

(Val-мин)/(макс-мин) = (ReturnValue-intervalBottom)/(intervalTop-intervalBottom)

, который решает для ReturnValue как:

returnValue = ((intervalTop-intervalBottom) * (val-min)/(max-min)) + intervalBottom 
4

Ответы: НЕ используйте десятичное значение для быстрых операций.

Есть ли причина, почему поплавок или двойной не работает для вас?

2

В дополнение к тому, что не используется Decimal, как уже было предложено, вы можете отправлять ваши значения max/min в другое место, чтобы вам не нужны все эти Abs-звонки повсюду.

Я подозреваю, что только его часть необходимо выполнить повторно - это умножение с плавающей запятой (или продолжение) с помощью добавления с плавающей запятой. Все остальное может быть предварительно проверено & предварительно рассчитано.

+0

вы так правы! – Germstorm 2008-12-04 10:14:06

7

Только тысячи значений? Вам действительно нужно оптимизировать это дальше? Я не могу себе представить, что это на самом деле узкое место в настоящий момент. Вы профилировали приложение, чтобы проверить, что это действительно проблема?

Учитывая, что метод O (1), вы не собираетесь выполнять самую радикальную оптимизацию, на которую обычно нацеливаете, - улучшая сложность.

Сказав это - когда вы называете это тысячами раз, любое значение остается постоянным? Например, вы используете один и тот же min и max несколько раз? Если это так, вы можете создать класс, который принимает эти значения в конструкторах и прекомпьютах, что он может, затем имеет метод, принимающий остальные параметры. Это немного улучшит ситуацию, но я вернусь к исходному моменту - только беспокоиться об этом, если это на самом деле вызывает проблемы.

1

Этот код выглядит более сложным, чем это действительно должно быть. Формула такова:

, который намного проще, чем ваша версия (и работает для интегральных типов). Там нет условных ветвей (вызов Math.Min) или вызовов метода. ОК, он предполагает, что intervalTop < intervalBottom и min < макс. Если intervalTop, intervalBottom, min и max являются постоянными для набора значений, вы можете предварительно скопировать (intervalBottom - intervalTop) и (max - min) и использовать эти результаты для всех вызовов функции. Другие накладные расходы, которые вы можете устранить, - это встроить функцию в вызывающий цикл. Я не знаю, что C# (или, скорее, JIT-компилятор) делает о методах вложения, чтобы это могло произойти уже под капотом.

Если вы можете жить с использованием типов данных int или float, одним из возможных решений является использование SIMD, но это будет означать написание собственного кода ассемблера, который может нарушить ваши требования.

Смежные вопросы