This is possible, but not all ready-made tools are present in the standard C ++ library.
Standard iterators provide discrete access. Therefore, we need some way to discretize the domain of the function while preserving the possibility of random access. For example, to use arithmetic with fixed accuracy - at least with the accuracy that is required in your task.
We need a random access iterator over "virtual" sequences, i.e. based on the results of a function call generated on the fly, and not on physical values in the memory. Such iterators or something similar, as far as I know, are present in Boost, but not in the current standard C ++ library. (What is strange, because the concept is very natural and in demand.)
The concept of strict equality does not work very well (to put it mildly) with floating arithmetic, so for binary search you will have to use boundary algorithms: std::lower_bound , std::upper_bound , std::equal_range ...
Here is an example of such an iterator written “on the knee” (it may well be that some operations are “redundant”, that is, the algorithms used will not be in demand, but I did not ask myself this question, but immediately implemented “more”)
#include <cstdint> #include <iterator> template <typename F, typename ARG, std::uintmax_t PRECISION> struct FunctionIterator { using iterator_category = std::random_access_iterator_tag; using value_type = ARG; using difference_type = std::intmax_t; using pointer = ARG *; using reference = ARG; F f; std::intmax_t x; FunctionIterator(F f, ARG x) : f(std::move(f)), x((std::intmax_t) (x * PRECISION)) {} ARG arg() const { return (ARG) x / PRECISION; } ARG operator *() const { return f(arg()); } FunctionIterator &operator++() { ++x; return *this; } FunctionIterator &operator++(int) { FunctionIterator old = *this; ++*this; return old; } FunctionIterator &operator +=(std::intmax_t rhs) { x += rhs; return *this; } friend FunctionIterator operator +(const FunctionIterator &lhs, std::intmax_t rhs) { return FunctionIterator(lhs) += rhs; } FunctionIterator &operator--() { --x; return *this; } FunctionIterator &operator--(int) { FunctionIterator old = *this; --*this; return old; } FunctionIterator &operator -=(std::intmax_t rhs) { x -= rhs; return *this; } friend FunctionIterator operator -(const FunctionIterator &lhs, std::intmax_t rhs) { return FunctionIterator(lhs) -= rhs; } friend std::intmax_t operator -(const FunctionIterator &lhs, const FunctionIterator &rhs) { return lhs.x - rhs.x; } friend bool operator ==(const FunctionIterator &lhs, const FunctionIterator &rhs) { return lhs.x == rhs.x; } friend bool operator !=(const FunctionIterator &lhs, const FunctionIterator &rhs) { return lhs.x != rhs.x; } friend bool operator <(const FunctionIterator &lhs, const FunctionIterator &rhs) { return lhs.x < rhs.x; } };
Having obtained such an iterator, we will be able to transfer it to standard algorithms, including to perform a search.
An example of using it to perform a binary search on a segment, say [-100, 100] with an accuracy of 0.001
#include <algorithm> #include <iostream> double f(double x) { return x * x * x; } int main() { FunctionIterator<double (*)(double), double, 1000> a(f, -100), b(f, 100), c = std::lower_bound(a, b, 5.0); std::cout << c.arg() << " " << *c << std::endl; }
Conclusion
1.71 5.00021
Increasing the multiplier to 100000 we get
1.70998 5.00004
And so on, trying to monitor the danger of overflow std::intmax_t .
F(m) == need, but a comparison ofneed - F(m) < EPS. - witaway pmwhile, butforwith a hard number of iterations. - witaway