Finding a function for linear growth that gradually plateaus

2k Views Asked by At

I need to draw a very particular kind of line (approximating it using a logarithmic curve is not going to be sufficient). Any help would be very gratefully appreciated!

https://i.stack.imgur.com/0lfAy.png

The line will be straight from 0,0 to Point_A.

It will be a curve from Point_A to 1,1. The gradient of this curved section will initially be the same as the gradient of the straight section, but will decrease gradually (linearly), reaching 0 when x=1.

Point_A will always have X and Y coordinates in the range 0 to 1. Point_A will also always be above the straight line from 0,0 to 1,1 (ie. in the half of the chart shaded darker), in case that makes a difference.

For any value of X between 0 and 1, I need a formula to calculate Y. In fact, it really just needs to be for any vaue of X between Ax and 1, since the straight section is relatively trivial.

https://i.stack.imgur.com/UQ2EU.png

Here's what I've tried so far... I'm extending the line from 0,0 to A, to find the value of Y when X is 1 (see the pink line). When X=1, Y = Ay / Ax eg. Y = 0.5 / 0.25 = 2

The pink straight line is always above the ideal curved line, but by how much? I now know that when X=1, the difference between the Y values of my straight line and my ideal curved line is Ay / Ax (which is 2 in this example) . At X=Ax, the difference is 0. For any given point between them, the difference will be somewhere between - but it's not a linear relationship.

For any given point on the line (Point_P), I'm finding 1 - Px (Line_B), and expressing it as a proportion of 1 - Ax (Line_C). B = (Px - Ax) / (1 - Ax)

When Px=Ax, B=0. When Px=1, B=1.

If I raise B to the power of 1.5, and subtract that from the straightline formula, I get the correct result here - but it only holds true for this example. eg. If X=0.5, B = (0.5 - 0.25) / (1 - 0.25) = 0.33333 B ^ 1.5 = 0.19245 Y = (0.5 * 0.5 / 0.25) - 0.19245 = 0.80755

So, how on Earth do I calculate the right exponent to use? Or am I doing something totally wrong to begin with?

2

There are 2 best solutions below

0
On

From your description, the part of your curve from $A$ to $(1,1)$ has to be a function whose derivative is linear:

The gradient of this curved section will initially be the same as the gradient of the straight section, but will decrease gradually (linearly), reaching 0 when x=1.

This means the curve itself must be a parabola described by a quadratic formula:

$$f(x)=ax^2+bx+c,$$

which has 3 parameters. Now you want that function to go through 2 given points ($A$ and $(1,1)$) and to have given derivatives at these points (same quote as above).

That means you have $4$ conditions, but only 3 variables, which means the system of equations is likely overdetermined. If I didn't make an error (which is unfortunately likely), your conditions can be only fullfilled if

$$y_A=\frac{2x_A}{x_A+1}.$$

To summarize: You problem can't be solved with the given conditions in most cases, it is overdetermined. You need to relax one condition at least.

0
On

In Wolfram Mathematica 12.0, after defining the following function:

ExtractData[source_, color_, x0_, y0_, Δx_, Δy_] :=

  Module[{col, data, dist, image, max, pos},

         image = Import[source];
         col = DominantColors[image, ColorCoverage -> .001];
         dist = ColorDistance[col, color];
         pos = Position[dist, Min[dist]][[1, 1]];

         data = Sort[PixelValuePositions[image, col[[pos]], .01]];
         data = Transpose[data] - First[data];

         max = {Max[data[[1]]], Max[data[[2]]]};
         Transpose[{x0, y0} + data {Δx, Δy} / max]

        ];

just write:

data = ExtractData["https://i.imgur.com/qcgqnEs.png", Blue, 0, 0, 1, 1];

fdata = FindFormula[data, x]

ListPlot[{data, Table[{x, fdata}, {x, 0, 1, .001}]}, 
         AspectRatio -> Automatic, 
         AxesLabel -> {x, y}, 
         PlotStyle -> {Blue, Red}]

to obtain:

-0.00185706 + 2.13586 x - 4.71704 x^2. + 51.0843 x^3. - 236.744 x^4. + 526.852 x^5. - 621.588 x^6. + 375.795 x^7. - 91.819 x^8.

enter image description here

which is the best function that approximates the points of the approximate starting graph (in blue the start graph, almost completely superimposed by the red end graph).

The power of FindFormula is that it almost instantly manages to identify the type of function that best approximates the experimental data among a multitude (obtainable by sum, product, composition, inversion of basic functions, i.e. polynomials, trigonometric, exponential functions). In fact, having decided to approximate the data with an eighth degree polynomial, it's sufficient to apply the least squares method:

{xi, yi} = Transpose[data];

NMinimize[Total[(yi - (a + b xi + c xi^2 + d xi^3 + e xi^4 + 
                 f xi^5 + g xi^6 + h xi^7 + i xi^8))^2], {a, b, c, d, e, f, g, h, i}]

by which the coefficients are quickly calculated:

{0.00183742, {a -> -0.00185708, b -> 2.13586, c -> -4.71707, d -> 51.0845, e -> -236.745, f -> 526.853, g -> -621.59, h -> 375.796, i -> -91.8193}}

which are essentially the same as those obtained automatically.

Of course, no one forbids to settle for a polynomial with a lesser degree, essentially this depends on the circumstances in which one operates. Anyway, by adding this other simple code:

frames = Table[coeff = Table[ToExpression[StringJoin["c", ToString[i]]], {i, 0, n}];
               fdata = Total[coeff Table[x^i, {i, 0, n}]];
               sol = NMinimize[Total[(y - fdata)^2 /. {x -> xi, y -> yi}], coeff][[2]];
               ListPlot[{data, Table[{x, fdata /. sol}, {x, 0, 1, .001}]}, 
                         AspectRatio -> Automatic,
                         AxesLabel -> {x, y},  
                         PlotLegends -> Placed[Style[StringJoin["polynomial of degree ", 
                                                     ToString[n]], Red], {.7, .5}],
                         PlotRange -> {{0, 1}, {0, 1}},
                         PlotStyle -> {Blue, Red}], 
               {n, 0, 8}];

Export["animation.gif", frames, "AnimationRepetitions" -> ∞, "DisplayDurations" -> 1];

you can visually realize how "wrong" you are:

enter image description here