anushri
anushri

Reputation: 67

F# negative indices in array

In my application there is a need to precompute and keep trigonometric function values for some particular angle parameters, the range varies from -90 to 180 degree. I can create arrays(one for each sine, cos etc) which will store value for -90 angle on 0th index and while retrieving I can subtract 90 from the index.

but is there any other way in F# to specify range of index, if we want to use [-90 .. 180] so that I can have more meaningful implementation.

considering alternate solution, will usage of dictionary be as fast as usage of simple 2D arrays.

Upvotes: 2

Views: 319

Answers (3)

Søren Debois
Søren Debois

Reputation: 5688

The easiest way is to simply make some function which given some i returns a pre-computed value of sin i:

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

To lookup the sine of, say, 42, you simply do table 42.

anushri and Tomasz both mention using Maps instead of Arrays, but in my experience, these are not good candidates for storing precomputed values, as they are much slower than Arrays. Let's try:

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) 

When I run this, array_table is roughly 8 times faster than no_table and 22 times faster than 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

Upvotes: 0

Tomasz Jaskuλa
Tomasz Jaskuλa

Reputation: 16033

If I understand well your problem you would need to retrieve precomputed values by the key/index which is a given angle going from -90 to 180. Something like this ?

let value = precomputed.[-90]

You could use Map for that. F# maps are implemented as immutable AVL trees, an efficient data structure which forms a self-balancing binary tree. This can be very efficient if you have a precomputed data and you need to look up by key fre­quently. Its immutabil­ity in this case ensures that the sta­tic data can­not be mod­i­fied by mis­take and has lit­tle impact to per­for­mance as you never need to mutate it once initialized. However if you need to modify it frequently I would advice you to use a regular .NET Dictionary because they are based on hashtable which has a better performance than AVL trees.

You could turn the list into the map where the key would be the angle and the value would be the precomputed one :

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

Where f is the function doing the precomputation. So you obtain your precomputed map for every angle something like that.

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

And you can access the procomputed sin value like that

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

Upvotes: 2

Daniel Fabian
Daniel Fabian

Reputation: 3838

A little index arithmetic will be of use:

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

since it's inline the overhead is going to be very, very small. And you can just use myArray.[idx -90]. (you might have to write different modulo values, but you get the picture)

Upvotes: 1

Related Questions