The way I understand your setup makes it seem like there's a simpler approach. The survival vector should be monotonic. So if you want the life expectancy to be, say, 8 years, then you want the 8th entry to be 1/2.
Clear[multiplier];
multiplier[le_Integer, qx_?(VectorQ[#, NumericQ] &)] :=
Block[{x},
First @ Sort @
Select[
x /. NSolve[Drop[FoldList[#1 (1 + #2) &, 1, - x * qx], -1][[le]] == 0.5, x, Reals],
0 < # &]
]
Example
multiplier[8, qx1]
(*
0.940737
*)
Since this is using approximate reals, there may be some boundary issues occasionally. You also might want to add some sanity checks. For instance,
Table[multiplier[x, qx1], {x, 2, 20}]
(*
{10.4778, 5.50098, 3.3954, 2.27372, 1.62347, 1.21458, 0.940737,
0.748985, 0.610999, 0.508783, 0.430586, 0.36941, 0.320942, 0.28194,
0.249964, 0.223317, 0.200685, 0.181273, 0.164589}
*)
The first few multipliers would make the entries in qx1
greater than 1
. In those cases, the function multiplier
should print an error message I suppose.
Update 29 Dec 2014
In a comment, the OP was interested in adapting the above method to an interpolated mortality curve. Here's a way.
I'm unfamiliar with the standard way of interpolating mortality, but linear or exponential seem likely candidates. So one of these two, with the multiplier built into the InterpolatingFunction
is the way to set it up:
Interpolation[
Transpose[{Range@Length@qx1, Drop[FoldList[#1 (1 + #2) &, 1, -x*qx1], -1]}],
InterpolationOrder -> 1]
Exp @* Interpolation[
Transpose[{Range@Length@qx1, Log @ Drop[FoldList[#1 (1 + #2) &, 1, -x*qx1], -1]}],
InterpolationOrder -> 1]
(* Use Composition[Exp, Interpolation[<..>] in V9 or earlier *)
Now the set up I really want is that the interpolation should be a function of a vector of annual probabilities of death qx
. So the definition I will use is
lxIF[qx_?(VectorQ[#1, NumericQ] &)] :=
lxIF[qx] =
Evaluate[
Exp @* Interpolation[
Transpose[{Range@Length@qx, Log @ Drop[FoldList[#1 (1 + #2) &, 1, -#*qx], -1]}],
InterpolationOrder -> 1]
] &;
lx[x_, qx_?(VectorQ[#1, NumericQ] &)] := lxIF[qx][x];
A few things need mentioning. First,I replaced the multiplier x
by the Function
argument #
. Second, we will be using this function many times, so used memoization to cache the interpolation in lxIF[qx]
the first time it is computed so that it will be reused instead of recomputed. Finally, the function call lxIF[qx][x]
replaces the argument #
in lxIF[qx]
by x
and returns an InterpolatingFunction
that is a function of life expectancy.
To calculate the probability of surviving 8 years for a multiplier x = 0.9
, use
lxIF[qx1][0.9][8]
(* 0.516127 *)
To find the multiplier for the median life expectancy to be 8.5 years, use
FindRoot[lxIF[qx1][x][8.5] == 0.5, {x, 1.}]
(* {x -> 0.833662} *)
A general use function can be constructed thus:
multiplier2[le_?NumericQ, qx_?(VectorQ[#1, NumericQ] &)] :=
Block[{x}, x /. FindRoot[lxIF[qx][x][le] == 0.5, {x, 1.}]]
Note that FindRoot
works well here because interpolating functions have derivatives. Even though lxIF
has a discontinuous derivative, it is strictly monotonic, which makes root-finding easy.
The mean life expectancy for a given multiplier x
can be computed with
meanLE[x_?NumericQ, qx_?(VectorQ[#1, NumericQ] &)] :=
NIntegrate[lxIF[qx][x][le], {le, 1, Length[qx]}];
FindRoot
works on it, too.
SOlve
. Always check that it's what you think it is. $\endgroup$