2015-04-21 5 views
2

В моем приложении необходимо предварительно скомпоновать и сохранять значения тригонометрической функции для некоторых определенных параметров угла, диапазон варьируется от -90 до 180 градусов. Я могу создать массивы (по одному для каждого синуса, cos и т. Д.), Который сохранит значение для угла -90 на 0-м индексе, а при извлечении я могу вычесть 90 из индекса.Отрицательные индексы F # в массиве

но есть ли другой способ в F # указать диапазон индекса, если мы хотим использовать [-90 .. 180] , чтобы у меня была более значимая реализация.

Рассматривая альтернативное решение, использование словаря будет таким же быстрым, как использование простых 2D-массивов.

+0

Вы проблема заключается в определении диапазона от -90 до 180 или извлечения по ключевому слову, как 'let value = precomputed. [- 90]' –

+0

yes Я хочу извлечь предварительно вычислимые значения для заданного угла. let value = precomputed. [- 90] – anushri

+0

Обычно [индексированные свойства] (https://msdn.microsoft.com/en-us/library/dd233202.aspx) (включая «Элемент») можно использовать для создания пользовательских индексаторов , Но я не уверен в их накладных расходах, если программа * это * сильно ограничена производительностью. Вы уверены, что стандартные тригонометрические функции являются узким местом производительности? Является ли скорость действительно проблемой здесь? Вы работаете на аппаратном обеспечении, где эти функции работают медленно? – Vandroiy

ответ

1

Маленький индекс арифметика будет полезен:

let inline idx i = (i + 270) % 270 

, так как это inline накладные расходы будет очень, очень мало. И вы можете просто использовать myArray.[idx -90]. (вам, возможно, придется писать разные значения по модулю, но вы получите изображение)

+2

Обратите внимание, что это приведет к сбою на входах, меньших, чем -270, с помощью оператора '%' по умолчанию. (Что может быть или не совсем нормально для данного прецедента) – Vandroiy

+0

Право, THX, полностью забыл об этом. –

2

Если я хорошо понимаю вашу проблему, вам нужно было бы получить предварительно вычисленные значения с помощью ключа/индекса, который является заданным углом от -90 до 180. Что-то вроде этого ?

let value = precomputed.[-90] 

Для этого вы можете использовать Map. Карты F # реализованы как непреложные деревья AVL, эффективная структура данных, которая формирует самобалансирующееся двоичное дерево. Это может быть очень эффективным, если у вас есть предварительно вычисляемые данные, и вам нужно часто искать ключ. Его неизменность в этом случае гарантирует, что статические данные не могут быть изменены по ошибке и мало влияют на производительность, поскольку вам никогда не нужно мутировать ее после инициализации. Однако, если вам нужно часто его изменять, я бы посоветовал вам использовать обычный .NET-словарь, потому что они основаны на хэш-таблице, которая имеет лучшую производительность, чем деревья AVL.

Вы можете включить список в карту, где ключ будет угол и значение будет предварительно вычисленная один:

let precomputedValus f = 
    [for i in -90..180 -> 
      i, f(i)] 
      |> Map.ofList 

Где f функция делает Предвычисление. Таким образом, вы получаете свою заранее рассчитанную карту для каждого угла, что-то вроде этого.

let sinValues = precomputedValus (fun e -> sin (float e)) 

И вы можете получить доступ к procomputed значение греха, как этот

> sinValues.[-90];; 
val it : float = -0.8939966636 
+0

значения неизменяемы, поэтому я считаю, что словарь не требуется. Я выяснил решение, данное вами, вполне подходящее спасибо для этого ..... теперь я думаю, что с этим могу сравнить производительность и сделать некоторые выводы. – anushri

+0

Да, карта подойдет сильно –

+1

Вопрос запрашивает ввод в градусах; 'sin' принимает радианный вход. Кроме того, обратите внимание, что это медленнее, чем прямое вычисление на основе типичных машин x86 и использует их. (Я только что провел грубую пробную версию, и прекомпция вызвала значительное замедление до менее 40% от первоначальной скорости. Конечно, это может зависеть от аппаратного обеспечения, кэширования, предсказания ветвлений из-за порядка ввода и т. Д.) – Vandroiy

0

Самый простой способ это просто сделать некоторые функции, которые дают некоторые i возвращает заранее вычисленное значение sin i:

let array_table = 
    let a = Array.init 271 (fun i -> sin <| float (i-90)) 
    fun i -> a.[i+90] 

Чтобы найти синус, скажем, 42, вы просто делаете table 42.

anushri и Tomasz, которые упоминают использование карт вместо массивов, но, по моему опыту, они не являются хорошими кандидатами для хранения предварительно вычисляемых значений, так как они много медленнее, чем массивы.Давайте попробуем:

let map_table = 
    let m = Seq.init 271 (fun i -> i-90, sin <| float i) |> Map.ofSeq 
    fun i -> Map.find i m 

let no_table = 
    fun i -> sin (float i) 

// Benchmarking code omitted (100000 lookups of each value in -90..270) 

Когда я запускаю это, array_table примерно в 8 раз быстрее, чем no_table и 22 раз быстрее, чем map_table:

> fsharpi --optimize+ memo.fsx 
map_table 
Real: 00:00:02.922, CPU: 00:00:02.924, GC gen0: 4, gen1: 0 
no_table 
Real: 00:00:01.091, CPU: 00:00:01.091, GC gen0: 3, gen1: 0 
array_table 
Real: 00:00:00.130, CPU: 00:00:00.130, GC gen0: 3, gen1: 0