function_inverse_mediant_inequality.sf 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
  1. #!/usr/bin/ruby
  2. # Daniel "Trizen" Șuteu
  3. # Date: 28 July 2018
  4. # https://github.com/trizen
  5. # Compute the inverse of any function by narrowing the search using the mediant inequality:
  6. # a/b < (a+c)/(b+d) < c/d
  7. # This algorithm, on average, takes more steps to converge than the binary search algorithm.
  8. # See also:
  9. # https://en.wikipedia.org/wiki/Farey_sequence
  10. # https://en.wikipedia.org/wiki/Stern%E2%80%93Brocot_tree
  11. # https://en.wikipedia.org/wiki/Mediant_(mathematics)
  12. func mediant_inverse (n, f, min=0, max=n, prec=192) {
  13. local Number!PREC = prec.numify
  14. (min, max) = (max, min) if (min > max)
  15. var (ln, ld) = min.nude
  16. var (rn, rd) = max.nude
  17. loop {
  18. var m = (ln+rn)/(ld+rd)
  19. var c = (f(m) <~> n)
  20. if (c < 0) {
  21. (ln, ld) = m.nude
  22. }
  23. elsif (c > 0) {
  24. (rn, rd) = m.nude
  25. }
  26. else {
  27. return m
  28. }
  29. }
  30. }
  31. say mediant_inverse( 2, {|x| x.exp }) # solution to x for: exp(x) = 2
  32. say mediant_inverse( 43, {|x| x**2 }) # solution to x for: x^2 = 43
  33. say mediant_inverse(-43, {|x| x**3 }) # solution to x for: x^3 = -43
  34. # Find the value of x such that Li(x) = 100
  35. say mediant_inverse(100, {|x| Li(x) }, min: 1, max: 1e6, prec: 200) #=> 488.87190985280753190605086392033334827378018556409