bigdecimal 3.3.1 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bigdecimal.gemspec +6 -1
- data/ext/bigdecimal/bigdecimal.c +232 -289
- data/ext/bigdecimal/bigdecimal.h +43 -37
- data/ext/bigdecimal/div.h +192 -0
- data/ext/bigdecimal/extconf.rb +5 -2
- data/ext/bigdecimal/missing.h +4 -2
- data/ext/bigdecimal/ntt.h +191 -0
- data/lib/bigdecimal/jacobian.rb +2 -0
- data/lib/bigdecimal/ludcmp.rb +2 -0
- data/lib/bigdecimal/math.rb +760 -82
- data/lib/bigdecimal/newton.rb +2 -0
- data/lib/bigdecimal/util.rb +1 -1
- data/lib/bigdecimal.rb +113 -73
- data/sample/linear.rb +73 -37
- data/sample/nlsolve.rb +47 -30
- data/sample/pi.rb +2 -7
- data/sig/big_decimal.rbs +1502 -0
- data/sig/big_decimal_util.rbs +158 -0
- data/sig/big_math.rbs +423 -0
- metadata +8 -3
data/lib/bigdecimal/newton.rb
CHANGED
data/lib/bigdecimal/util.rb
CHANGED
data/lib/bigdecimal.rb
CHANGED
|
@@ -12,6 +12,9 @@ end
|
|
|
12
12
|
|
|
13
13
|
class BigDecimal
|
|
14
14
|
module Internal # :nodoc:
|
|
15
|
+
# Default extra precision for intermediate calculations
|
|
16
|
+
# This value is currently the same as BigDecimal.double_fig, but defined separately for future changes.
|
|
17
|
+
EXTRA_PREC = 16
|
|
15
18
|
|
|
16
19
|
# Coerce x to BigDecimal with the specified precision.
|
|
17
20
|
# TODO: some methods (example: BigMath.exp) require more precision than specified to coerce.
|
|
@@ -60,6 +63,55 @@ class BigDecimal
|
|
|
60
63
|
end
|
|
61
64
|
BigDecimal::NAN
|
|
62
65
|
end
|
|
66
|
+
|
|
67
|
+
# Iteration for Newton's method with increasing precision
|
|
68
|
+
def self.newton_loop(prec, initial_precision: BigDecimal.double_fig / 2, safe_margin: 2) # :nodoc:
|
|
69
|
+
precs = []
|
|
70
|
+
while prec > initial_precision
|
|
71
|
+
precs << prec
|
|
72
|
+
prec = (precs.last + 1) / 2 + safe_margin
|
|
73
|
+
end
|
|
74
|
+
precs.reverse_each do |p|
|
|
75
|
+
yield p
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Fast and rough conversion to float for mathematical calculations.
|
|
80
|
+
# Bigdecimal#to_f is slow when n_significant_digits is large.
|
|
81
|
+
# This is because to_f internally converts BigDecimal to String
|
|
82
|
+
# to get the exact nearest float representation.
|
|
83
|
+
# TODO: Remove this workaround when BigDecimal#to_f is optimized.
|
|
84
|
+
def self.fast_to_f(x) # :nodoc:
|
|
85
|
+
x.n_significant_digits < 40 ? x.to_f : x.mult(1, 20).to_f
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Calculates Math.log(x.to_f) considering large or small exponent
|
|
89
|
+
def self.float_log(x) # :nodoc:
|
|
90
|
+
Math.log(fast_to_f(x._decimal_shift(-x.exponent))) + x.exponent * Math.log(10)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Calculating Taylor series sum using binary splitting method
|
|
94
|
+
# Calculates f(x) = (x/d0)*(1+(x/d1)*(1+(x/d2)*(1+(x/d3)*(1+...))))
|
|
95
|
+
# x.n_significant_digits or ds.size must be small to be performant.
|
|
96
|
+
def self.taylor_sum_binary_splitting(x, ds, prec) # :nodoc:
|
|
97
|
+
fs = ds.map {|d| [0, BigDecimal(d)] }
|
|
98
|
+
# fs = [[a0, a1], [b0, b1], [c0, c1], ...]
|
|
99
|
+
# f(x) = a0/a1+(x/a1)*(1+b0/b1+(x/b1)*(1+c0/c1+(x/c1)*(1+d0/d1+(x/d1)*(1+...))))
|
|
100
|
+
while fs.size > 1
|
|
101
|
+
# Merge two adjacent fractions
|
|
102
|
+
# from: (1 + a0/a1 + x/a1 * (1 + b0/b1 + x/b1 * rest))
|
|
103
|
+
# to: (1 + (a0*b1+x*(b0+b1))/(a1*b1) + (x*x)/(a1*b1) * rest)
|
|
104
|
+
xn = xn ? xn.mult(xn, prec) : x
|
|
105
|
+
fs = fs.each_slice(2).map do |(a, b)|
|
|
106
|
+
b ||= [0, BigDecimal(1)._decimal_shift([xn.exponent, 0].max + 2)]
|
|
107
|
+
[
|
|
108
|
+
(a[0] * b[1]).add(xn * (b[0] + b[1]), prec),
|
|
109
|
+
a[1].mult(b[1], prec)
|
|
110
|
+
]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
BigDecimal(fs[0][0]).div(fs[0][1], prec)
|
|
114
|
+
end
|
|
63
115
|
end
|
|
64
116
|
|
|
65
117
|
# call-seq:
|
|
@@ -144,10 +196,10 @@ class BigDecimal
|
|
|
144
196
|
return BigDecimal(1)
|
|
145
197
|
end
|
|
146
198
|
|
|
147
|
-
|
|
199
|
+
limit = BigDecimal.limit
|
|
148
200
|
frac_part = y.frac
|
|
149
201
|
|
|
150
|
-
if frac_part.zero? && prec.zero?
|
|
202
|
+
if frac_part.zero? && prec.zero? && limit.zero?
|
|
151
203
|
# Infinite precision calculation for `x ** int` and `x.power(int)`
|
|
152
204
|
int_part = y.fix.to_i
|
|
153
205
|
int_part = -int_part if (neg = int_part < 0)
|
|
@@ -167,18 +219,19 @@ class BigDecimal
|
|
|
167
219
|
return neg ? BigDecimal(1) / ans : ans
|
|
168
220
|
end
|
|
169
221
|
|
|
170
|
-
|
|
222
|
+
result_prec = prec.nonzero? || [x.n_significant_digits, y.n_significant_digits, BigDecimal.double_fig].max + BigDecimal.double_fig
|
|
223
|
+
result_prec = [result_prec, limit].min if prec.zero? && limit.nonzero?
|
|
224
|
+
|
|
225
|
+
prec2 = result_prec + BigDecimal::Internal::EXTRA_PREC
|
|
171
226
|
|
|
172
227
|
if y < 0
|
|
173
|
-
inv = x.power(-y,
|
|
228
|
+
inv = x.power(-y, prec2)
|
|
174
229
|
return BigDecimal(0) if inv.infinite?
|
|
175
230
|
return BigDecimal::Internal.infinity_computation_result if inv.zero?
|
|
176
|
-
return BigDecimal(1).div(inv,
|
|
231
|
+
return BigDecimal(1).div(inv, result_prec)
|
|
177
232
|
end
|
|
178
233
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if frac_part.zero? && y.exponent < Math.log(prec) * 5 + 20
|
|
234
|
+
if frac_part.zero? && y.exponent < Math.log(result_prec) * 5 + 20
|
|
182
235
|
# Use exponentiation by squaring if y is an integer and not too large
|
|
183
236
|
pow_prec = prec2 + y.exponent
|
|
184
237
|
n = 1
|
|
@@ -191,16 +244,16 @@ class BigDecimal
|
|
|
191
244
|
break if n > int_part
|
|
192
245
|
xn = xn.mult(xn, pow_prec)
|
|
193
246
|
end
|
|
194
|
-
ans.mult(1,
|
|
247
|
+
ans.mult(1, result_prec)
|
|
195
248
|
else
|
|
196
|
-
if x > 1
|
|
249
|
+
if x > 1 && x.finite?
|
|
197
250
|
# To calculate exp(z, prec), z needs prec+max(z.exponent, 0) precision if z > 0.
|
|
198
251
|
# Estimate (y*log(x)).exponent
|
|
199
252
|
logx_exponent = x < 2 ? (x - 1).exponent : Math.log10(x.exponent).round
|
|
200
253
|
ylogx_exponent = y.exponent + logx_exponent
|
|
201
254
|
prec2 += [ylogx_exponent, 0].max
|
|
202
255
|
end
|
|
203
|
-
BigMath.exp(BigMath.log(x, prec2).mult(y, prec2),
|
|
256
|
+
BigMath.exp(BigMath.log(x, prec2).mult(y, prec2), result_prec)
|
|
204
257
|
end
|
|
205
258
|
end
|
|
206
259
|
|
|
@@ -217,15 +270,15 @@ class BigDecimal
|
|
|
217
270
|
return self if zero?
|
|
218
271
|
|
|
219
272
|
if prec == 0
|
|
220
|
-
|
|
273
|
+
limit = BigDecimal.limit
|
|
274
|
+
prec = n_significant_digits + BigDecimal.double_fig
|
|
275
|
+
prec = [limit, prec].min if limit.nonzero?
|
|
221
276
|
end
|
|
222
277
|
|
|
223
278
|
ex = exponent / 2
|
|
224
279
|
x = _decimal_shift(-2 * ex)
|
|
225
|
-
y = BigDecimal(Math.sqrt(x
|
|
226
|
-
|
|
227
|
-
precs << 2 + precs.last / 2 while precs.last > BigDecimal.double_fig
|
|
228
|
-
precs.reverse_each do |p|
|
|
280
|
+
y = BigDecimal(Math.sqrt(BigDecimal::Internal.fast_to_f(x)), 0)
|
|
281
|
+
Internal.newton_loop(prec + BigDecimal::Internal::EXTRA_PREC) do |p|
|
|
229
282
|
y = y.add(x.div(y, p), p).div(2, p)
|
|
230
283
|
end
|
|
231
284
|
y._decimal_shift(ex).mult(1, prec)
|
|
@@ -235,6 +288,7 @@ end
|
|
|
235
288
|
# Core BigMath methods for BigDecimal (log, exp) are defined here.
|
|
236
289
|
# Other methods (sin, cos, atan) are defined in 'bigdecimal/math.rb'.
|
|
237
290
|
module BigMath
|
|
291
|
+
module_function
|
|
238
292
|
|
|
239
293
|
# call-seq:
|
|
240
294
|
# BigMath.log(decimal, numeric) -> BigDecimal
|
|
@@ -248,7 +302,7 @@ module BigMath
|
|
|
248
302
|
#
|
|
249
303
|
# If +decimal+ is NaN, returns NaN.
|
|
250
304
|
#
|
|
251
|
-
def
|
|
305
|
+
def log(x, prec)
|
|
252
306
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :log)
|
|
253
307
|
raise Math::DomainError, 'Complex argument for BigMath.log' if Complex === x
|
|
254
308
|
|
|
@@ -259,60 +313,36 @@ module BigMath
|
|
|
259
313
|
return BigDecimal::Internal.infinity_computation_result if x.infinite?
|
|
260
314
|
return BigDecimal(0) if x == 1
|
|
261
315
|
|
|
262
|
-
prec2 = prec + BigDecimal
|
|
263
|
-
BigDecimal.save_limit do
|
|
264
|
-
BigDecimal.limit(0)
|
|
265
|
-
if x > 10 || x < 0.1
|
|
266
|
-
log10 = log(BigDecimal(10), prec2)
|
|
267
|
-
exponent = x.exponent
|
|
268
|
-
x = x._decimal_shift(-exponent)
|
|
269
|
-
if x < 0.3
|
|
270
|
-
x *= 10
|
|
271
|
-
exponent -= 1
|
|
272
|
-
end
|
|
273
|
-
return (log10 * exponent).add(log(x, prec2), prec)
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
x_minus_one_exponent = (x - 1).exponent
|
|
277
|
-
|
|
278
|
-
# log(x) = log(sqrt(sqrt(sqrt(sqrt(x))))) * 2**sqrt_steps
|
|
279
|
-
sqrt_steps = [Integer.sqrt(prec2) + 3 * x_minus_one_exponent, 0].max
|
|
316
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
280
317
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
# log(x) = -log((1 + X) / (1 - X)) where X = (x - 1) / (x + 1)
|
|
290
|
-
# log(x) = 2 * (X + X**3 / 3 + X**5 / 5 + X**7 / 7 + ...)
|
|
291
|
-
x = (x - 1).div(x + 1, sqrt_prec)
|
|
292
|
-
y = x
|
|
293
|
-
x2 = x.mult(x, prec2)
|
|
294
|
-
1.step do |i|
|
|
295
|
-
n = prec2 + x.exponent - y.exponent + x2.exponent
|
|
296
|
-
break if n <= 0 || x.zero?
|
|
297
|
-
x = x.mult(x2.round(n - x2.exponent), n)
|
|
298
|
-
y = y.add(x.div(2 * i + 1, n), prec2)
|
|
299
|
-
end
|
|
318
|
+
# Reduce x to near 1
|
|
319
|
+
if x > 1.01 || x < 0.99
|
|
320
|
+
# log(x) = log(x/exp(logx_approx)) + logx_approx
|
|
321
|
+
logx_approx = BigDecimal(BigDecimal::Internal.float_log(x), 0)
|
|
322
|
+
x = x.div(exp(logx_approx, prec2), prec2)
|
|
323
|
+
else
|
|
324
|
+
logx_approx = BigDecimal(0)
|
|
325
|
+
end
|
|
300
326
|
|
|
301
|
-
|
|
327
|
+
# Solve exp(y) - x = 0 with Newton's method
|
|
328
|
+
# Repeat: y -= (exp(y) - x) / exp(y)
|
|
329
|
+
y = BigDecimal(BigDecimal::Internal.float_log(x), 0)
|
|
330
|
+
exp_additional_prec = [-(x - 1).exponent, 0].max
|
|
331
|
+
BigDecimal::Internal.newton_loop(prec2) do |p|
|
|
332
|
+
expy = exp(y, p + exp_additional_prec)
|
|
333
|
+
y = y.sub(expy.sub(x, p).div(expy, p), p)
|
|
302
334
|
end
|
|
335
|
+
y.add(logx_approx, prec)
|
|
303
336
|
end
|
|
304
337
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
y = y.add(xn, prec)
|
|
314
|
-
end
|
|
315
|
-
y
|
|
338
|
+
private_class_method def _exp_binary_splitting(x, prec) # :nodoc:
|
|
339
|
+
return BigDecimal(1) if x.zero?
|
|
340
|
+
# Find k that satisfies x**k / k! < 10**(-prec)
|
|
341
|
+
log10 = Math.log(10)
|
|
342
|
+
logx = BigDecimal::Internal.float_log(x.abs)
|
|
343
|
+
step = (1..).bsearch { |k| Math.lgamma(k + 1)[0] - k * logx > prec * log10 }
|
|
344
|
+
# exp(x)-1 = x*(1+x/2*(1+x/3*(1+x/4*(1+x/5*(1+...)))))
|
|
345
|
+
1 + BigDecimal::Internal.taylor_sum_binary_splitting(x, [*1..step], prec)
|
|
316
346
|
end
|
|
317
347
|
|
|
318
348
|
# call-seq:
|
|
@@ -325,7 +355,7 @@ module BigMath
|
|
|
325
355
|
#
|
|
326
356
|
# If +decimal+ is NaN, returns NaN.
|
|
327
357
|
#
|
|
328
|
-
def
|
|
358
|
+
def exp(x, prec)
|
|
329
359
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :exp)
|
|
330
360
|
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :exp)
|
|
331
361
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
@@ -334,14 +364,24 @@ module BigMath
|
|
|
334
364
|
|
|
335
365
|
# exp(x * 10**cnt) = exp(x)**(10**cnt)
|
|
336
366
|
cnt = x < -1 || x > 1 ? x.exponent : 0
|
|
337
|
-
prec2 = prec + BigDecimal
|
|
367
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC + cnt
|
|
338
368
|
x = x._decimal_shift(-cnt)
|
|
339
369
|
|
|
340
|
-
#
|
|
341
|
-
#
|
|
342
|
-
# exp(x)
|
|
343
|
-
|
|
344
|
-
|
|
370
|
+
# Decimal form of bit-burst algorithm
|
|
371
|
+
# Calculate exp(x.xxxxxxxxxxxxxxxx) as
|
|
372
|
+
# exp(x.xx) * exp(0.00xx) * exp(0.0000xxxx) * exp(0.00000000xxxxxxxx)
|
|
373
|
+
x = x.mult(1, prec2)
|
|
374
|
+
n = 2
|
|
375
|
+
y = BigDecimal(1)
|
|
376
|
+
BigDecimal.save_limit do
|
|
377
|
+
BigDecimal.limit(0)
|
|
378
|
+
while x != 0 do
|
|
379
|
+
partial_x = x.truncate(n)
|
|
380
|
+
x -= partial_x
|
|
381
|
+
y = y.mult(_exp_binary_splitting(partial_x, prec2), prec2)
|
|
382
|
+
n *= 2
|
|
383
|
+
end
|
|
384
|
+
end
|
|
345
385
|
|
|
346
386
|
# calculate exp(x * 10**cnt) from exp(x)
|
|
347
387
|
# exp(x * 10**k) = exp(x * 10**(k - 1)) ** 10
|
data/sample/linear.rb
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
#!/usr/local/bin/ruby
|
|
2
|
-
# frozen_string_literal: false
|
|
3
|
-
|
|
4
1
|
#
|
|
5
2
|
# linear.rb
|
|
6
3
|
#
|
|
@@ -13,62 +10,101 @@
|
|
|
13
10
|
|
|
14
11
|
# :stopdoc:
|
|
15
12
|
require "bigdecimal"
|
|
16
|
-
require "bigdecimal/ludcmp"
|
|
17
13
|
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
#
|
|
14
|
+
# Requires gem matrix
|
|
15
|
+
require "matrix"
|
|
16
|
+
|
|
17
|
+
class PrecisionSpecifiedValue
|
|
18
|
+
# NOTE:
|
|
19
|
+
# Change following PREC if needed.
|
|
20
|
+
|
|
21
|
+
attr_reader :value
|
|
22
|
+
def initialize(value, prec)
|
|
23
|
+
@value = BigDecimal(value)
|
|
24
|
+
@prec = prec
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def unwrap(value)
|
|
28
|
+
PrecisionSpecifiedValue === value ? value.value : value
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def coerce(other)
|
|
32
|
+
[self.class.new(unwrap(other), @prec), self]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def abs
|
|
36
|
+
self.class.new(@value.abs, @prec)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def >(other)
|
|
40
|
+
@value > unwrap(other)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def <(other)
|
|
44
|
+
@value < unwrap(other)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def -(other)
|
|
48
|
+
self.class.new(@value.sub(unwrap(other), @prec), @prec)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def +(other)
|
|
52
|
+
self.class.new(@value.add(unwrap(other), @prec), @prec)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def *(other)
|
|
56
|
+
self.class.new(@value.mult(unwrap(other), @prec), @prec)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def quo(other)
|
|
60
|
+
self.class.new(@value.div(unwrap(other), @prec), @prec)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
return if __FILE__ != $0
|
|
23
65
|
|
|
24
|
-
include LUSolve
|
|
25
66
|
def rd_order(na)
|
|
26
|
-
|
|
27
|
-
|
|
67
|
+
printf("Number of equations ?") if(na <= 0)
|
|
68
|
+
ARGF.gets().to_i
|
|
28
69
|
end
|
|
29
70
|
|
|
30
|
-
na
|
|
31
|
-
zero = BigDecimal("0.0")
|
|
32
|
-
one = BigDecimal("1.0")
|
|
71
|
+
na = ARGV.size
|
|
33
72
|
|
|
34
73
|
while (n=rd_order(na))>0
|
|
35
74
|
a = []
|
|
36
|
-
as= []
|
|
37
75
|
b = []
|
|
38
76
|
if na <= 0
|
|
39
77
|
# Read data from console.
|
|
40
78
|
printf("\nEnter coefficient matrix element A[i,j]\n")
|
|
41
79
|
for i in 0...n do
|
|
42
|
-
|
|
80
|
+
a << n.times.map do |j|
|
|
43
81
|
printf("A[%d,%d]? ",i,j); s = ARGF.gets
|
|
44
|
-
|
|
45
|
-
as << BigDecimal(s)
|
|
82
|
+
BigDecimal(s)
|
|
46
83
|
end
|
|
47
84
|
printf("Contatant vector element b[%d] ? ",i)
|
|
48
85
|
b << BigDecimal(ARGF.gets)
|
|
49
86
|
end
|
|
50
87
|
else
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
end
|
|
61
|
-
b << BigDecimal(s[n])
|
|
62
|
-
end
|
|
88
|
+
# Read data from specified file.
|
|
89
|
+
printf("Coefficient matrix and constant vector.\n")
|
|
90
|
+
for i in 0...n do
|
|
91
|
+
s = ARGF.gets
|
|
92
|
+
printf("%d) %s",i,s)
|
|
93
|
+
s = s.split
|
|
94
|
+
a << n.times.map {|j| BigDecimal(s[j]) }
|
|
95
|
+
b << BigDecimal(s[n])
|
|
96
|
+
end
|
|
63
97
|
end
|
|
64
|
-
|
|
98
|
+
|
|
99
|
+
prec = 100
|
|
100
|
+
matrix = Matrix[*a.map {|row| row.map {|v| PrecisionSpecifiedValue.new(v, prec) } }]
|
|
101
|
+
vector = b.map {|v| PrecisionSpecifiedValue.new(v, prec) }
|
|
102
|
+
x = matrix.lup.solve(vector).map(&:value)
|
|
103
|
+
|
|
65
104
|
printf("Answer(x[i] & (A*x-b)[i]) follows\n")
|
|
66
105
|
for i in 0...n do
|
|
67
106
|
printf("x[%d]=%s ",i,x[i].to_s)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
s = s + as[i*n+j]*x[j]
|
|
71
|
-
end
|
|
72
|
-
printf(" & %s\n",(s-b[i]).to_s)
|
|
107
|
+
diff = a[i].zip(x).sum {|aij, xj| aij*xj }.sub(b[i], 10)
|
|
108
|
+
printf(" & %s\n", diff.to_s)
|
|
73
109
|
end
|
|
74
110
|
end
|
data/sample/nlsolve.rb
CHANGED
|
@@ -1,40 +1,57 @@
|
|
|
1
|
-
#!/usr/local/bin/ruby
|
|
2
|
-
# frozen_string_literal: false
|
|
3
|
-
|
|
4
1
|
#
|
|
5
2
|
# nlsolve.rb
|
|
6
3
|
# An example for solving nonlinear algebraic equation system.
|
|
7
4
|
#
|
|
8
5
|
|
|
9
6
|
require "bigdecimal"
|
|
10
|
-
|
|
11
|
-
include Newton
|
|
7
|
+
require_relative "linear"
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
9
|
+
# Requires gem matrix
|
|
10
|
+
require "matrix"
|
|
11
|
+
|
|
12
|
+
# :stopdoc:
|
|
13
|
+
|
|
14
|
+
def func((x, y)) # defines functions solved
|
|
15
|
+
[
|
|
16
|
+
x**2 + y**2 - 2,
|
|
17
|
+
(x - 1)**2 + (y + 1)**2 - 3
|
|
18
|
+
]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def jacobian(x, f, delta, prec)
|
|
22
|
+
dim = x.size
|
|
23
|
+
dim.times.map do |i|
|
|
24
|
+
xplus = Array.new(dim) {|j| x[i] + (j == i ? delta : 0) }
|
|
25
|
+
xminus = Array.new(dim) {|j| x[i] - (j == i ? delta : 0) }
|
|
26
|
+
yplus = f.call(xplus)
|
|
27
|
+
yminus = f.call(xminus)
|
|
28
|
+
yplus.zip(yminus).map {|p, m| (p - m).div(2 * delta, prec) }
|
|
29
|
+
end.transpose
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def nlsolve(initial_x, prec:, max_iteration: 100, &f)
|
|
33
|
+
initial_x = initial_x.map {|v| BigDecimal(v) }
|
|
34
|
+
x = initial_x
|
|
35
|
+
delta = BigDecimal(0.01)
|
|
36
|
+
calc_prec = prec + 10
|
|
37
|
+
max_iteration.times do |iteration|
|
|
38
|
+
# Newton step
|
|
39
|
+
jacobian = jacobian(x, f, delta, calc_prec)
|
|
40
|
+
matrix = Matrix[*jacobian.map {|row| row.map {|v| PrecisionSpecifiedValue.new(v, calc_prec) } }]
|
|
41
|
+
y = f.call(x)
|
|
42
|
+
vector = y.map {|v| PrecisionSpecifiedValue.new(v, calc_prec) }
|
|
43
|
+
dx = matrix.lup.solve(vector).map(&:value)
|
|
44
|
+
x_prev = x
|
|
45
|
+
x = x.zip(dx).map {|xi, di| xi.sub(di, prec) }
|
|
46
|
+
movement = x_prev.zip(x).map {|xn, xi| (xn - xi).abs }.max
|
|
47
|
+
delta = [movement, delta].min.mult(1, 10)
|
|
48
|
+
break if movement.zero? || movement.exponent < -prec
|
|
33
49
|
end
|
|
50
|
+
x
|
|
34
51
|
end
|
|
35
52
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
p
|
|
53
|
+
initial_value = [1, 1]
|
|
54
|
+
ans = nlsolve(initial_value, prec: 100) {|x| func(x) }
|
|
55
|
+
diff = func(ans).map {|v| v.mult(1, 10) }
|
|
56
|
+
p(ans:)
|
|
57
|
+
p(diff:)
|
data/sample/pi.rb
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
|
-
#!/usr/local/bin/ruby
|
|
2
|
-
# frozen_string_literal: false
|
|
3
|
-
|
|
4
1
|
#
|
|
5
2
|
# pi.rb
|
|
6
3
|
#
|
|
7
4
|
# Calculates 3.1415.... (the number of times that a circle's diameter
|
|
8
|
-
# will fit around the circle)
|
|
5
|
+
# will fit around the circle)
|
|
9
6
|
#
|
|
10
7
|
|
|
11
8
|
require "bigdecimal"
|
|
12
9
|
require "bigdecimal/math.rb"
|
|
13
10
|
|
|
14
|
-
include BigMath
|
|
15
|
-
|
|
16
11
|
if ARGV.size == 1
|
|
17
12
|
print "PI("+ARGV[0]+"):\n"
|
|
18
|
-
p PI(ARGV[0].to_i)
|
|
13
|
+
p BigMath.PI(ARGV[0].to_i)
|
|
19
14
|
else
|
|
20
15
|
print "TRY: ruby pi.rb 1000 \n"
|
|
21
16
|
end
|