|
1 | | -#!/usr/local/bin/ruby |
2 | 1 | # frozen_string_literal: false |
3 | 2 |
|
4 | 3 | # |
|
7 | 6 | # |
8 | 7 |
|
9 | 8 | require "bigdecimal" |
10 | | -require "bigdecimal/newton" |
11 | | -include Newton |
12 | | - |
13 | | -class Function # :nodoc: all |
14 | | - def initialize() |
15 | | - @zero = BigDecimal("0.0") |
16 | | - @one = BigDecimal("1.0") |
17 | | - @two = BigDecimal("2.0") |
18 | | - @ten = BigDecimal("10.0") |
19 | | - @eps = BigDecimal("1.0e-16") |
20 | | - end |
21 | | - def zero;@zero;end |
22 | | - def one ;@one ;end |
23 | | - def two ;@two ;end |
24 | | - def ten ;@ten ;end |
25 | | - def eps ;@eps ;end |
26 | | - def values(x) # <= defines functions solved |
27 | | - f = [] |
28 | | - f1 = x[0]*x[0] + x[1]*x[1] - @two # f1 = x**2 + y**2 - 2 => 0 |
29 | | - f2 = x[0] - x[1] # f2 = x - y => 0 |
30 | | - f <<= f1 |
31 | | - f <<= f2 |
32 | | - f |
| 9 | +require_relative "linear" |
| 10 | + |
| 11 | +# Requires gem matrix |
| 12 | +require "matrix" |
| 13 | + |
| 14 | +# :stopdoc: |
| 15 | + |
| 16 | +def func((x, y)) # defines functions solved |
| 17 | + [ |
| 18 | + x**2 + y**2 - 2, |
| 19 | + (x - 1)**2 + (y + 1)**2 - 3 |
| 20 | + ] |
| 21 | +end |
| 22 | + |
| 23 | +def jacobian(x, f, delta, prec) |
| 24 | + dim = x.size |
| 25 | + dim.times.map do |i| |
| 26 | + xplus = Array.new(dim) {|j| x[i] + (j == i ? delta : 0) } |
| 27 | + xminus = Array.new(dim) {|j| x[i] - (j == i ? delta : 0) } |
| 28 | + yplus = f.call(xplus) |
| 29 | + yminus = f.call(xminus) |
| 30 | + yplus.zip(yminus).map {|p, m| (p - m).div(2 * delta, prec) } |
| 31 | + end.transpose |
| 32 | +end |
| 33 | + |
| 34 | +def nlsolve(initial_x, prec:, max_iteration: 100, &f) |
| 35 | + initial_x = initial_x.map {|v| BigDecimal(v) } |
| 36 | + x = initial_x |
| 37 | + delta = BigDecimal(0.01) |
| 38 | + calc_prec = prec + 10 |
| 39 | + max_iteration.times do |iteration| |
| 40 | + # Newton step |
| 41 | + jacobian = jacobian(x, f, delta, calc_prec) |
| 42 | + matrix = Matrix[*jacobian.map {|row| row.map {|v| PrecisionSpecifiedValue.new(v, calc_prec) } }] |
| 43 | + y = f.call(x) |
| 44 | + vector = y.map {|v| PrecisionSpecifiedValue.new(v, calc_prec) } |
| 45 | + dx = matrix.lup.solve(vector).map(&:value) |
| 46 | + x_prev = x |
| 47 | + x = x.zip(dx).map {|xi, di| xi.sub(di, prec) } |
| 48 | + movement = x_prev.zip(x).map {|xn, xi| (xn - xi).abs }.max |
| 49 | + delta = [movement, delta].min.mult(1, 10) |
| 50 | + break if movement.zero? || movement.exponent < -prec |
33 | 51 | end |
| 52 | + x |
34 | 53 | end |
35 | 54 |
|
36 | | -f = BigDecimal.limit(100) |
37 | | -f = Function.new |
38 | | -x = [f.zero,f.zero] # Initial values |
39 | | -n = nlsolve(f,x) |
40 | | -p x |
| 55 | +initial_value = [1, 1] |
| 56 | +ans = nlsolve(initial_value, prec: 100) {|x| func(x) } |
| 57 | +diff = func(ans).map {|v| v.mult(1, 10) } |
| 58 | +p(ans:) |
| 59 | +p(diff:) |
0 commit comments