How can I encode this equation in Java?
750=(1-(1+j)^(-10))/j*86
This one performs the calculation using the
j
in the range of 10 -5 to 1. It works with BigDecimal
s rounding divisions with 21 decimal places and working with a margin of error of 10 -5 in the gross value of q0
found to accept the solution.
Here is the code:
import java.math.BigDecimal;
import java.math.RoundingMode;
class Calculo {
private static final BigDecimal CEM = new BigDecimal(100);
private static final BigDecimal DOIS = new BigDecimal(2);
private static final int PRECISAO_CALCULO = 21;
private static final int PRECISAO_FINAL = 5;
private static final BigDecimal DELTA =
BigDecimal.ONE.divide(BigDecimal.TEN.pow(PRECISAO_FINAL));
private static final BigDecimal financiamento(int n, BigDecimal j, BigDecimal p) {
BigDecimal a = BigDecimal.ONE.add(j).pow(n);
BigDecimal b = BigDecimal.ONE.divide(
a,
PRECISAO_CALCULO,
RoundingMode.HALF_EVEN);
return BigDecimal.ONE
.subtract(b)
.multiply(p)
.divide(j, PRECISAO_CALCULO, RoundingMode.HALF_EVEN);
}
private static final BigDecimal taxaFinanciamento(
BigDecimal q0,
int n,
BigDecimal p)
{
BigDecimal j1 = DELTA;
BigDecimal j2 = BigDecimal.ONE;
BigDecimal q1 = financiamento(n, j1, p);
BigDecimal q2 = financiamento(n, j2, p);
BigDecimal m1 = q1.subtract(q0);
BigDecimal m2 = q2.subtract(q0);
if (m1.abs().compareTo(DELTA) <= 0) return j1;
if (m2.abs().compareTo(DELTA) <= 0) return j2;
for (int i = 0; i < 50; i++) {
if (m1.signum() == m2.signum()) {
throw new ArithmeticException("Fora do intervalo"
+ ": q1=[" + q1 + "], j1=[" + j1 + "], m1=[" + m1 + "]"
+ ", q2=[" + q2 + "], j2=[" + j2 + "], m2=[" + m2 + "]"
+ ", i=" + i);
}
BigDecimal j3 = j1.add(j2).divide(DOIS);
BigDecimal q3 = financiamento(n, j3, p);
BigDecimal m3 = q3.subtract(q0);
if (m3.abs().compareTo(DELTA) <= 0) return j3;
if (m3.signum() == m1.signum()) {
q1 = q3;
j1 = j3;
} else {
q2 = q3;
j2 = j3;
}
}
throw new ArithmeticException("Não convergiu"
+ ": q1=[" + q1 + "], j1=[" + j1 + "], m1=[" + m1 + "]"
+ ", q2=[" + q2 + "], j2=[" + j2 + "], m2=[" + m2 + "]");
}
private static void teste(int q0, int p, int n) {
BigDecimal bdq0Ideal = new BigDecimal(q0);
BigDecimal bdp = new BigDecimal(p);
BigDecimal j = taxaFinanciamento(bdq0Ideal, n, bdp);
BigDecimal bdq0Obtido = financiamento(n, j, bdp);
System.out.println("----------");
System.out.println("q0-ideal=[" + bdq0Ideal + "]");
System.out.println("p=[" + bdp + "]");
System.out.println("j=[" + j + "]");
System.out.println("q0-obtido=[" + bdq0Obtido + "]");
System.out.println("----------");
System.out.println();
}
public static void main(String[] args) {
teste(750, 86, 10);
teste(750, 85, 10);
}
}
Here's the output:
----------
q0-ideal=[750]
p=[86]
j=[0.025690244208984076976776123046875]
q0-obtido=[749.999997639096753068406]
----------
----------
q0-ideal=[750]
p=[85]
j=[0.023429261477030813694000244140625]
q0-obtido=[749.999996056141438963093]
----------
This output means that:
For a value of p = 86 and a 0 strong 750 750 750
For a value of
85 and a 0 a j = 2.3429261 ...% value, it was calculated that a
I'm using the trapeze method to solve this equation. This method also takes the name "secant method".
The trapeze approach closely matches the Newton-Raphson Algorithm , but instead of using derivative uses the difference between two given points to fetch the next tentative point. It is not all the functions that fit the use of this method to find its roots. This equation, for example, is not.
Newton-Raphson Method:
"Animation of Newton's method" by Ralf Pfeifer is licensed under CC-BY-SA 3.0
Trapezoids method:
"Illustration of the secant method" by Jitse Niesen, public domain.
I played at WolframAlpha to know one of the real roots and see if I can get close.
import java.util.function.DoubleUnaryOperator;
public class ResolucaoTrapezio {
private Double raiz;
private Integer iteracoesTotal;
private Double errRaiz;
public boolean achouRaiz() {
return raiz != null;
}
public Double getRaiz() {
return raiz;
}
public Double getErrRaiz() {
return errRaiz;
}
public Integer getIteracoesTotal() {
return iteracoesTotal;
}
public ResolucaoTrapezio(DoubleUnaryOperator func, double x0, double x1, double err, int iteracoes) {
achaRaiz(func, x0, x1, err, iteracoes);
}
public void achaRaiz(DoubleUnaryOperator func, double x0, double x1, double err, int iteracoes) {
double y0, y1;
y0 = func.applyAsDouble(x0);
if (diffErrAceitavel(y0, err)) {
iteracoesTotal = 0;
raiz = x0;
errRaiz = -y0;
return;
}
for (int it = 0; it < iteracoes; it++) {
y1 = func.applyAsDouble(x1);
if (diffErrAceitavel(y1, err)) {
iteracoesTotal = it + 1;
raiz = x1;
errRaiz = -y1;
return;
}
// entrou em laço infinito =(
if (y1 == y0) {
break;
}
// próximo passo, achando o próximo x
double x2 = x0 - y0*(x1 - x0)/(y1 - y0);
// atualizando: x_{i} <-- x_{i-1}
x0 = x1;
x1 = x2;
// atualizando: y0 recebe o último y encontrado
y0 = y1;
}
iteracoesTotal = null;
raiz = null;
errRaiz = null;
return;
}
private static boolean diffErrAceitavel(double y0, double err) {
return Math.abs(y0) < err;
}
}
This object, from the constructor, attempts to find the answer using the trapeze method. It has some deliberate limitations to avoid divergent processing, such as rectangle detection and number of iterations. It also considers that the root is a close enough number of zero according to a past error. I am using 7 precision decimal places, so the error is guaranteed to be less than 0.0000001
.
If the root can not be found, the value of getRaiz()
and getErrRaiz()
will be null. I'm always saving how many iterations I used to arrive at the conclusion that I got to the root or diverged in getIteracoesTotal()
.
For your function, I used the following formula:
(j) -> {
double q0 = 750;
double n = 10;
double p = 86;
return p*(1 - Math.pow(1 + j, -n)) / j - q0;
}
If you want to find the value of another variable, it is just for the variable as parameter and set the value of j
. For example hypothetical:
(n) -> {
double q0 = 750;
double p = 86;
double j = 0.0256902;
return p*(1 - Math.pow(1 + j, -n)) / j - q0;
}
An example call to this object is as follows:
public static void main(String[] args) {
ResolucaoTrapezio resTrap = new ResolucaoTrapezio((j) -> {
double q0 = 750;
double n = 10;
double p = 86;
return p*(1 - Math.pow(1 + j, -n)) / j - q0;
}, -1.8, -1.5, 0.00000001, 1000);
System.out.println("resolução da equação: " + resTrap.getRaiz() + ", nessa quantidade de iterações: " + resTrap.getIteracoesTotal() + " (erro " + resTrap.getErrRaiz() + ")");
}
The output obtained was:
resolução da equação: -1.756371998522421, nessa quantidade de iterações: 10 (erro -3.410605131648481E-13)
Here, in the trapeze method, finding the first two good values is fundamental. The @VictorStafusa suggested that I should start with 0.25 and 0.5.
Testing @VictorStafusa's suggestion:
resolução da equação: null, nessa quantidade de iterações: 1000 (erro null)
Well, it diverged ...
Then we can try to guess the starting points for this function ... Let's start from 0.001
to 0.9
as x0
, 0.0001
step. Let x1
differs from x0
delta
between 0.00001
to 0.5
x0
varying with each step 0.00001
.
I can do this through the following for
:
for (double x0 = 0.001; x0 <= 0.9; x0 += 0.0001) {
for (double delta = 0.0001; delta <= 0.5; delta += 0.0001) {
double x1 = x0 + delta;
// processamento desejado aqui
}
}
Trying to find the root ...
public static void main(String[] args) {
DoubleUnaryOperator f = (j) -> {
double q0 = 750;
double n = 10;
double p = 86;
return p*(1 - Math.pow(1 + j, -n)) / j - q0;
};
int divergencias = 0;
int convergencias = 0;
int totalIteracoesAcc = 0;
for (double x0 = 0.001; x0 <= 0.9; x0 += 0.0001) {
for (double delta = 0.0001; delta <= 0.5; delta += 0.0001) {
double x1 = x0 + delta;
ResolucaoTrapezio resTrap = new ResolucaoTrapezio(f, x0, x1, 0.00000001, 1000);
totalIteracoesAcc += resTrap.getIteracoesTotal();
if (resTrap.achouRaiz()) {
if (convergencias < 10) {
System.out.println(String.format("ACHOU RAIZ... parâmetros: x0 = %f x1 = %f; raiz = %f (err = %f); iterações %d", x0, x1, resTrap.getRaiz(), resTrap.getErrRaiz(), resTrap.getIteracoesTotal()));
}
convergencias++;
} else {
if (divergencias < 10) {
System.out.println(String.format("não achou raiz... parâmetros: x0 = %f x1 = %f; iterações %d", x0, x1, resTrap.getIteracoesTotal()));
}
divergencias++;
}
System.out.println(String.format("Já rodamos %d iterações ao todo; %d convergências, %d divergências", totalIteracoesAcc, convergencias, divergencias));
}
}
}
Output:
ACHOU RAIZ... parâmetros: x0 = 0,001000 x1 = 0,001100; raiz = 0,025690 (err = -0,000000); iterações 6 Já rodamos 6 iterações ao todo; 1 convergências, 0 divergências ACHOU RAIZ... parâmetros: x0 = 0,001000 x1 = 0,001200; raiz = 0,025690 (err = -0,000000); iterações 6 Já rodamos 12 iterações ao todo; 2 convergências, 0 divergências ACHOU RAIZ... parâmetros: x0 = 0,001000 x1 = 0,001300; raiz = 0,025690 (err = -0,000000); iterações 6 Já rodamos 18 iterações ao todo; 3 convergências, 0 divergências [... omitido por questão de porque sim ...]
Well, we found the root. Happy? I am = D
Out of curiosity, if the algorithm were to run for all possible combinations of x0
and x1
, the time it would take ...
Rodamos ao todo -1825970073 iterações ao todo; 12818639 convergências, 32136361 divergências Tempo total: 519060 ms
Well, I broke the integer, I'm going to put a long
that should hold. I also go with long
to the number of convergences and divergences just in case ...
New result, now with overflow of integers:
Rodamos ao todo 32533768295 iterações ao todo; 12818639 convergências, 32136361 divergências Tempo total: 492871 ms
I'm one of the biggest supporters of that, when you're dealing with financial math and money, you should use BigDecimal
.
However, the resolution approach to this problem was via numerical methods. It is so much that I can not find the real root, but I found a value that is at a maximum error of 1e-7
of 0
. The calculations and results of these numerical methods carry with them an intrinsic error that does not fit the exact type BigDecimal
, but it is the scientific type double
.
Read more: