1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jadira.quant.coremath;
17
18 import org.jadira.quant.api.CompositeFunction;
19 import org.jadira.quant.api.QuantitativeFunction;
20 import org.jadira.quant.exception.IntervalBisectionOutOfRangeException;
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 public class IntervalBisection implements CompositeFunction<Double, QuantitativeFunction<Double, Double>> {
43
44 private final double precision;
45 private final int iterations;
46 private final double lowerBound;
47 private final double higherBound;
48 private PrecisionStrategy precisionStrategy;
49
50 public enum PrecisionStrategy {
51 TO_INTERVAL, BETWEEN_RESULTS;
52 }
53
54 public IntervalBisection(double lowerBound, double higherBound) {
55 this(lowerBound, higherBound, 20);
56 }
57
58
59
60
61
62
63
64 public IntervalBisection(double lowerBound, double higherBound, int iterations) {
65
66 this(lowerBound, higherBound, iterations, 0.001D, PrecisionStrategy.TO_INTERVAL);
67 }
68
69
70
71
72
73
74
75
76
77 public IntervalBisection(double lowerBound, double higherBound, int iterations, double precision, PrecisionStrategy precisionStrategy) {
78
79 if (iterations < 1) {
80 throw new IllegalArgumentException("iterations must be greater than 1");
81 }
82 if (Double.compare(precision, 0.0D) < 0) {
83 throw new IllegalArgumentException("precision must be a positive number");
84 }
85 if (precisionStrategy == null) {
86 throw new IllegalArgumentException("precisionStrategy may not be null");
87 }
88
89 this.lowerBound = lowerBound;
90 this.higherBound = higherBound;
91 this.iterations = iterations;
92 this.precision = precision;
93 this.precisionStrategy = precisionStrategy;
94 }
95
96 public double getLowerBound() {
97 return lowerBound;
98 }
99
100 public double getHigherBound() {
101 return higherBound;
102 }
103
104 public int getIterations() {
105 return iterations;
106 }
107
108 public double getPrecision() {
109 return precision;
110 }
111
112 @Override
113 public Double apply(QuantitativeFunction<Double, Double> computeFunction) {
114
115 final double resultA = computeFunction.apply(lowerBound);
116 final double resultB = computeFunction.apply(higherBound);
117
118 if (resultA != 0.0D && resultB != 0.0D
119 && (Double.compare(resultA, 0.0D) ^ Double.compare(0.0D, resultB)) != 0) {
120 throw new IntervalBisectionOutOfRangeException(lowerBound, higherBound);
121 }
122
123 double currentLower = lowerBound;
124 double currentHigher = higherBound;
125
126 double currentMiddle = Double.NaN;
127 double midValueResult = Double.NaN;
128 double preceedingMidValueResult = Double.NaN;
129
130 for (int i = 0; i < iterations; i++) {
131
132 preceedingMidValueResult = midValueResult;
133
134 currentMiddle = currentLower + 0.5 * (currentHigher - currentLower);
135 midValueResult = computeFunction.apply(currentMiddle);
136
137 if (resultA * midValueResult < 0) {
138 currentHigher = currentMiddle;
139 } else if (resultA * midValueResult > 0) {
140 currentLower = currentMiddle;
141 }
142
143 if (PrecisionStrategy.TO_INTERVAL == precisionStrategy) {
144 if (Math.abs(midValueResult) <= precision) {
145 break;
146 }
147 } else if ((PrecisionStrategy.BETWEEN_RESULTS == precisionStrategy || null == precisionStrategy)
148 && (Math.abs(midValueResult - preceedingMidValueResult) <= precision)) {
149 break;
150 }
151 }
152 return currentMiddle;
153 }
154 }