Adding Tolerances to Excel LAMBDA functions - excel-formula

I created a LAMBDA function called LOANAMT to calculate loan amounts recursively for a situation in which you need to borrow to fund the loan payment (yes, I know this can be solved algebraically - I'm trying to learn about LAMBDA).
I incorporated a tolerance check as my escape clause; if the next round of interest calculations is very close to the previous round, the LAMBDA exits. This worked fine with a hard-coded tolerance level of 0.001:
=LAMBDA(opening_balance, base_rate, [interest],
LET(
_int, IF(ISOMITTED(interest), 0, interest),
_new_close, opening_balance + _int,
_new_int, _new_close * base_rate,
_closing_balance, IF(ABS(_new_int-_int)<0.001, _new_close,LOANAMT(opening_balance,base_rate,_new_int)),
_closing_balance
)
)
Gave me 106.38290 where opening_balance = 100, base_rate = 6%, which approximately agrees with the algebraic solution.
However, when I tried incorporating the tolerance as a parameter of the LAMBDA so that it could be adjusted easily, I got a #NUM error.
=LAMBDA(opening_balance, base_rate, tolerance, [interest],
LET(
_int, IF(ISOMITTED(interest), 0, interest),
_new_close, opening_balance + _int,
_new_int, _new_close * base_rate,
_closing_balance, IF(ABS(_new_int-_int)<tolerance, _new_close,LOANAMT2(opening_balance,base_rate,_new_int)),
_closing_balance
)
)
Could anyone explain what's going wrong and help me fix this?
Thanks.

The second version doesn't pass the tolerance value to the recursive call to LOANAMT2, hence the #NUM! error.
This works:
LOANAMT2=LAMBDA(opening_balance, base_rate, tolerance, [interest],
LET(
_int, IF(ISOMITTED(interest), 0, interest),
_new_close, opening_balance + _int,
_new_int, _new_close * base_rate,
_closing_balance, IF(ABS(_new_int-_int)<tolerance, _new_close,LOANAMT2(opening_balance,base_rate,tolerance,_new_int)),
_closing_balance
)
)

Related

Dealing with numbers outside of uint256 range

i am trying to build compound formula with solidity but I have came across a problem, given enough days, the amount is compounded, amount will overflow uint256 range.
I have read that as long as final result is in the range of uint256, it should be fine but it does not seem to be the case. here is the calculation that i am trying to do
(100 * (100+8)^100) * 10^(6 - 2*100)
as a formula it looks like this:
( BASE * ( (1 * 10^MULTIPLIER) + (YIELD * 10^MULITPLIER) ) ^ COMPOUNDED_DAYS ) * ( 10 ^ (DECIMALS - MULTIPLIER * COMPOUNDED_DAYS) )
as mentioned this is compound formula with 6 decimals, but when i run this in playground i get "0" as a result. what's the problem, and if there is problem how can i fix it?
The reason is that your number becomes smaller than one and the integer turns it into zero
The caret sign ^ represents a bitwise XOR - not an exponential multiplication. If you want to calculate "to the power of", use two asterisks **.
Example: "10 to the power of 2" in Solidity: 10 ** 2

Divide a value in excel by a set of preset values to find out how many of each are needed

I am curious if there is a way to make my life easier. In excel I am producing a total value, say 750 and need to find out how many orders of pipe I need from values of 50,100,200,250,500. Is there anyway to have excel take a value and then return how many of each of these numbers I would need, so for the 750 case 1 500 and 1 250?
Currently the solution is just worked out in my head
Assuming you want to try to fit pipes in decreasing order of size,and that you have access to the required functions, you can use Reduce as demonstrated here to step through the sizes and successively divide by each one although the formula is a little laboured:
=LET(pipes,{500;250;200;100;50},reqd,750,DROP(REDUCE(0,pipes,
LAMBDA(a,c,VSTACK(a,QUOTIENT(reqd-IF(ROWS(a)>1,SUM(DROP(a,1)*TAKE(pipes,ROWS(a)-1)),0),c)))),1))
As pointed out by #Jos Woolley, this may not give you the answer you want if the total is something like 749. It will fit as many values in as possible and give a result 500+200 total 700 (remainder 49). You could fix it perhaps by rounding up to the next multiple of 50.
For the example of 823, you would have:
=LET(pipes,{500;250;200;100;50},reqd,CEILING(823,MIN(pipes)),DROP(REDUCE(0,pipes,
LAMBDA(a,c,VSTACK(a,QUOTIENT(reqd-IF(ROWS(a)>1,SUM(DROP(a,1)*TAKE(pipes,ROWS(a)-1)),0),c)))),1))
which gives 500+250+100=850.
Well I've got a bit obsessed with this now and I am determined to get a lambda working to find the optimal answer! I have been looking at the brute-force solution to finding the minimum number of coins required to make up a given total in the reference mentioned previously and have managed to translate it into a lambda using Reduce:
Mincoins1= LAMBDA(coins, m, v,
IF(
v <= 0,
0,
REDUCE(
999,
coins,
LAMBDA(a, c,
IF(v >= c, LET(mc, mincoins1.mincoins1(coins, m, v - c) + 1, IF(mc < a, mc, a)), a)
)
)
)
)
This does give the correct answer, 2, for the case when you want to make up a value of 400 from the list of pipes given. The next step will be to modify the code to return the list of pipes which give that total (200,200).
https://www.enjoyalgorithms.com/blog/minimum-coin-change
Here is the lambda modified to return a string containing the chosen pipes:
Mincoins2= LAMBDA(coins, m, v,
IF(
v <= 0,
"",
REDUCE(
rept("x",999),
coins,
LAMBDA(a, c,
IF(v >= c, LET(mc, c&"|"&mincoins2.mincoins2(coins, m, v - c), IF(len(mc) < len(a), mc, a)), a)
)
)
)
);
It does work BUT (and this is a big but) it hits a limit as soon as the value to be produced exceeds 1000 and you get a #value error. Disappointing. But interesting I think as a proof of concept.
Not sure I understand the question but lets try.
if you have 1 450 to divide, have a formula that divides 1 450 with you highest lenght (750) and then round it down.
so the formula would be something of the line: = rounddown(1 450 / 750; 0)
you will then get the answer that you need 1 of the length 750.
then keep the info about how much length you have remaining. So a formula like:
=1 450 - 750 * [the answer from previous formula = 1]. this would sum to 700.
then start over with the same thing, but divide 700 with 500 (second largest size).
Your question is extremely difficult: one might think for this easy solution, starting with value_begin:
amount_of_500 = value_begin DIV 500; // integer division
temp = value_begin - 500 * amount_of_500;
amount_of_250 = temp DIV 250; // again integer division
temp = temp - 250 * amount_of_250;
amount_of_200 = temp DIV 200; // again integer division
temp = temp - 200 * amount_of_200;
...
However, this will not work because of the value 200, which is far too close to 250: just start with value_begin equal to 400 (algorithm solution : 250 + 100 + 50, while best solution : 200 + 200).
Are you sure you need both 200 and 250 as possible numbers to divide by? If yes, you might have a serious problem getting this implemented.

Why KL divergence is giving nan? Is it some mathematical error or my input data is incorrect?

In the following code s returns nan. As each value in Q<1 so it returns a negative value when I take its log. Does it mean that I can not calculate KL divergence with these values of P and Q or can I fix it?
`P= np.array([1.125,3.314,2.7414])
Q=np.array([0.42369288, 0.89152044, 0.60905852])
for i in range(len(P)):
if P[i] != 0 and Q[i]!=0:
s= P[i] *np.log(P[i]/Q[i])
print("s: ",s)`
First of, P and Q should describe probability mass functions, meaning that each element should be in the interval [0,1] and they each should sum to 1, which is not the case for your examples.
The second np.log is wrong. Is there a reason you put it there or was it a typo? It should be P[i]*np.log(P[i]/Q[i]). You also want to perform the sum over all these terms for i.
Finally there is a technical issue of what to do if P[i] = 0. In that case np.log(0) would cause problems. The actual contribution of the term should be 0 in that case (because lim_{x->0} x*log(x) = 0). You can guarantee this, e.g. by handling this case specially with an if clause.
The case of Q[i] = 0 would cause similar issues, however the KL divergence doesn't exist if Q[i] = 0, but not P[i] = 0, anyway.

Scipy.integrate gives odd results; are there best practices?

I am still struggling with scipy.integrate.quad.
Sparing all the details, I have an integral to evaluate. The function is of the form of the integral of a product of functions in x, like so:
Z(k) = f(x) g(k/x) / abs(x)
I know for certain the range of integration is between tow positive numbers. Oddly, when I pick a wide range that I know must contain all values of x that are positive - like integrating from 1 to 10,000,000 - it intgrates fast and gives an answer which looks right. But when I fingure out the exact limits - which I know sice f(x) is zero over a lot of the real line - and use those, I get another answer that is different. They aren't very different, though I know the second is more accurate.
After much fiddling I got it to work OK, but then needed to add in an exopnentiation - I was at least getting a 'smooth' answer for the computed function of z. I had this working in an OK way before I added in the exponentiation (which is needed), but now the function that gets generated (z) becomes more and more oscillatory and peculiar.
Any idea what is happening here? I know this code comes from an old Fortran library, so there must be some known issues, but I can't find references.
Here is the core code:
def normal(x, mu, sigma) :
return (1.0/((2.0*3.14159*sigma**2)**0.5)*exp(-(x-
mu)**2/(2*sigma**2)))
def integrand(x, z, mu, sigma, f) :
return np.exp(normal(z/x, mu, sigma)) * getP(x, f._x, f._y) / abs(x)
for _z in range (int(z_min), int(z_max) + 1, 1000):
z.append(_z)
pResult = quad(integrand, lb, ub,
args=(float(_z), MU-SIGMA**2/2, SIGMA, X),
points = [100000.0],
epsabs = 1, epsrel = .01) # drop error estimate of tuple
p.append(pResult[0]) # drop error estimate of tuple
By the way, getP() returns a linearly interpolated, piecewise continuous,but non-smooth function to give the integrator values that smoothly fit between the discrete 'buckets' of the histogram.
As with many numerical methods, it can be very sensitive to asymptotes, zeros, etc. The only choice is to keep giving it 'hints' if it will accept them.

excel implement an if statement inside another if statement

I need to implement an IF statement inside another IF statement in excel!
=IF(DataInput!$D$5 = "James Herriot Freight",
ROUND( (.0025 * I4 + .75 * O4) * T4, 2),
IF( DataInput!$D$5 = "Siegfried Farnon Transport",
ROUND( 1.5+ IF(O4 > 1.0, (O4 – 1.0) * 0.5, 0) * T4, 2),
"ERROR: Invalid Freight Type"
) )
The inner IF statement is doing a completely different check, unreleated to DataInput!$D$5. Is this even possible in excel, or do I need to look in to maybe rewriting the whole thing with and, and or as part of what I need to do? Or do I have an error somewhere else?

Resources