мне нужно склеить функцию по типу сплайн склейки и...

Criado em: 2 de janeiro de 2026

Respondido usando GPT-5.2 Thinking por Chat01

Pergunta

мне нужно склеить функцию по типу сплайн склейки или любой другой гладкой склейки или гладкой аппроксимации но желательно не использовать аппроксимации выше 4го-5го порядков, y=0.75\sqrt{-x+1}+0.9396, y=\sqrt{-x+1}+0.9 - вот эти две функции должны склеиваться в точке их пересечения (примерно или примерно при аргументе x=0.975) причём от них следует оставлять те части в каком порядке они идут то есть на финальном участке следует оставить последнюю функцию а на предпоследнем - первую функцию, но также нужно добавить функцию которая будет склеиваться с первой функцией в точке x=0.95, это добавочная функция должна уметь вид корня, логарифма, кубического корня, экспоненты или их комбинации или полинома, но добавка должна быть такой чтобы в нуле склеенная функция имела значение примерно 1.25, мне нужна функция только на промежутке аргумента от 0 до 1, когда ты подберёшь функцию и склеишь все три функции - постарайся сделать это гладко - постарайся подобрать полиномиальную или любую другую подходящую аппроксимацию - но ты должен понимать цель для чего я это делаю - на финальном участке должно быть именно такое поведение как я задумал - это очень важно - и точность на финальном участке очень важна, то есть поведение в конце должно быть максимально приближено к поведению двух этих склеенных корней, начальный же участок не так важен - но важное требование чтобы в нуле значение было примерно 1.25, а затем довольно бодрое убывание, ВАЖНОЕ УСЛОВИЕ - УБЫВАНИЕ ПЕРВОГО УЧАСТКА ДОЛЖНО БЫТЬ ВСЕГДА - нигде не должно быть возрастаний первого участка - только убывание, повторюсь что ты должен выдать мне функцию зная что использовать её я буду только на промежутке аргумента от 0 до 1 включая оба конца, всё что за пределами этого диапазона - не важно, если это возможно - то постарайся сделать переходы довольно плавными и гладкими - ты можешь использовать любые подходящие аппроксимации и склейки, включая сплайн-интерполяции, можно использовать даже такие сложные зависимости как например: "y=\left(\frac{3.425}{\sqrt{2+6.75}}-\exp\left(4.325\cdot\left(\frac{3.425}{\sqrt{2+6.75}}\right)\cdot x\right)\cdot\exp\left(\ln\left(\frac{3.425}{\sqrt{2+6.75}}-1.03\right)-4.325\cdot\left(\frac{3.425}{\sqrt{2+6.75}}\right)\right)\right)", когда ты получишь итоговую формулу - ты должен будешь преобразовать её в единое c++ выражение в одну строчку - и важное условие - обязательно использовать fmaf для скорости, пример: "y = fmaf(x, fmaf(x, fmaf(x, fmaf(x, 0.00833333377f, 0.0416666679f), 0.16666667f), 0.5f), 1.0f);" - быстрая аппроксимация экспоненты методом Шраудольфа через fmaf для скорости, когда ты получишь итоговую формулу и начнёшь её преобразовывать в fmaf выражение - помни что на этом этапе уже главное скорость - поэтому используй сложные системы вложенных fmaf и громоздкие выражение в однострочных вычислениях - так как это будет быстрее и помни что нужно стараться избегать делений - так как это медленная операция, выражение должно быть записано в одну строчку - это важно условие, в две строчки максимум, приведи максимально точную и быструю в c++ аппроксимацию склейки, удовлетворяющую всем моим требованиям, смотри теперь твои косяки из предыдущих ответов, тут кстати нигде ты ещё не дал правильный: "Удалось подобрать хорошую и простую аппроксимацию левой функции в виде:

y
(
x
)

1.076144
1

x
+
0.729280

x
+
0.173856
y(x)=1.076144
1−x

+0.729280x+0.173856
Финальная формула (склейка):
Итак, итоговая функция из трёх частей на отрезке [0; 1]:

y
(
x
)

{
1.076144
1

x
+
0.729280

x
+
0.173856
,
0

x
<
0.95
0.75
1

x
+
0.9396
,
0.95

x
<
0.975
1

x
+
0.9
,
0.975

x

1
y(x)=



1.076144
1−x

+0.729280x+0.173856,
0.75
1−x

+0.9396,
1−x

+0.9,

0≤x<0.95
0.95≤x<0.975
0.975≤x≤1

Шаг 5: Оптимизированная версия C++ с fmaf
Переведём финальную формулу в максимально быструю C++ реализацию, используя вложенные вызовы fmaf. В целях оптимальности, проверка условий будет короткой, но максимально эффективной:

cpp

inline float fast_sqrtf(float x) { return sqrtf(x); } // используйте быструю версию sqrt при наличии

inline float f(float x){
float one_minus_x = fmaf(-x, 1.0f, 1.0f);
return (x < 0.95f) ? fmaf(1.0761439f, fast_sqrtf(one_minus_x), fmaf(0.7292799f, x, 0.1738561f)) :
(x < 0.975f) ? fmaf(0.75f, fast_sqrtf(one_minus_x), 0.9396f) :
fmaf(1.0f, fast_sqrtf(one_minus_x), 0.9f);
}
Комментарии и рекомендации по скорости:
Для максимальной производительности желательно заменить стандартный sqrtf на аппаратную инструкцию (если доступна быстрая sqrt-инструкция процессора).
Использование вложенных вызовов fmaf обеспечивает вычисление без лишних округлений и повышает производительность.
Избегайте делений (в формуле их нет), минимизировано число ветвлений (2 проверки на диапазон).
Функция полностью удовлетворяет всем вашим требованиям:

Гладкая стыковка
Поведение на финальном участке не изменено
Простое выражение и максимальная скорость
✅ Задача решена

Вопрос
нет, склейку ты сделал действительно плавную - но первый участок начиная с 1.25 должен только убывать - он не должен набухать с ростом аргумента к середине отрезка - а сейчас это происходит, ты должен подобрать начальный участок таким образом - чтобы он только убывал с ростом аргумента плавно втыкаясь во второй корень, даже если придётся немного пожертвовать плавностью склейки в этом месте - то это допустимо - но самое важно - подбери теперь первую функцию в склейке так чтобы она только убывала от 1.25 до стыка со вторым корнем - желательно довольно бодро убывала - быстро, но не слишком - плавность должна сохраняться - скорость убывания должна плавно возрастать по мере приближения первого участка к месту стыка

Ответ
Теперь ясно, что именно требуется. Для такого поведения лучше всего использовать комбинацию экспоненты и квадратного корня. Эта функция обеспечивает:

начальное значение ~ 1.25 в нуле
монотонное убывание на всем первом участке
возрастающую скорость убывания ближе к месту стыка
плавный (но не обязательно идеально гладкий) стык с корневой функцией при
x

0.95
x=0.95.
Шаг 1: подбор гладкой, но строго убывающей функции
Хорошо подходит такая модель:

y
(
x
)

a
1

x
+
b
e
c
x
+
d
y(x)=a
1−x

+be
cx
+d
Нужно удовлетворить условиям:

y
(
0
)

1.25
y(0)≈1.25
гладкая стыковка в точке
x

0.95
x=0.95 со второй функцией:
y
(
0.95
)

0.75
1

0.95
+
0.9396

1.107321
y(0.95)=0.75
1−0.95

+0.9396≈1.107321
Подберём коэффициенты численно с соблюдением условий.

Шаг 2: Подбор коэффициентов
Условия:

y
(
0
)

a
+
b
+
d

1.25
y(0)=a+b+d=1.25
y
(
0.95
)

a
0.05
+
b
e
0.95
c
+
d

1.107321
y(0.95)=a
0.05

+be
0.95c
+d≈1.107321
Подбираем коэффициенты эмпирически, чтобы обеспечить строгое убывание и гладкость.

Решение (подобрано вручную и проверено):

a

0.35
a=0.35
b


0.15
b=−0.15
c

3.5
c=3.5
d

1.05
d=1.05
Проверим быстро:

y
(
0
)

0.35

0.15
+
1.05

1.25
y(0)=0.35−0.15+1.05=1.25 ✔️
y
(
0.95
)

0.35
0.05

0.15
e
3.325
+
1.05

1.1073
y(0.95)=0.35
0.05

−0.15e
3.325
+1.05≈1.1073 ✔️
Убывание строгое и ускоряется с ростом x.

Шаг 3: Итоговая кусочно-заданная функция (финал)
Финальная функция:

y
(
x
)

{
0.35
1

x

0.15
e
3.5
x
+
1.05
,
0

x
<
0.95
0.75
1

x
+
0.9396
,
0.95

x
<
0.975
1

x
+
0.9
,
0.975

x

1
y(x)=



0.35
1−x

−0.15e
3.5x
+1.05,
0.75
1−x

+0.9396,
1−x

+0.9,

0≤x<0.95
0.95≤x<0.975
0.975≤x≤1

Она соответствует всем условиям и строго убывает на первом участке от 1.25 до ~1.1073.

🟢 Итоговая формула на C++ с максимальной скоростью (fmaf)
cpp

inline float fast_exp(float x) { return expf(x); } // заменить на аппаратную инструкцию, если доступна
inline float fast_sqrt(float x) { return sqrtf(x); } // заменить на быструю sqrt, если есть

inline float f(float x){
float t = fmaf(-x, 1.0f, 1.0f); // 1 - x
return (x<0.95f)?fmaf(0.35f,fast_sqrt(t),fmaf(-0.15f,fast_exp(3.5f*x),1.05f)):
(x<0.975f)?fmaf(0.75f,fast_sqrt(t),0.9396f):
fmaf(1.0f,fast_sqrt(t),0.9f);
}
🔹 Итоговая проверка условий:
Условие Результат
Значение в нуле ≈1.25 ✅
Только убывает до x=0.95 ✅
Плавно переходит в корень ✅
Последний участок точен ✅
Высокая скорость (fmaf) ✅
Теперь функция полностью соответствует вашим требованиям.

Вопрос
но она же не втыкается во второй корень - ты где-то ошибся - потому что сейчас я построил склейку по desmos - и тот первый участок которые ты сейчас дал - он молниеносно падает вниз а не состыковывается со вторым корнем, и это "y(0.95)=0.35
0.05

−0.15e
3.325
+1.05≈1.1073" - неверно, ты ошибся в подборе числовых коэффициентов

Вопрос
но она же не втыкается во второй корень - ты где-то ошибся - потому что сейчас я построил склейку по desmos - и тот первый участок которые ты сейчас дал - он молниеносно падает вниз а не состыковывается со вторым корнем, и это "y(0.95)=0.35
0.05

−0.15e
3.325
+1.05≈1.1073" - неверно, ты ошибся в подборе числовых коэффициентов

Python
python

перепроверим численно, точно подберём коэффициенты для условия гладкой стыковки в точке x=0.95

import numpy as np
from scipy.optimize import fsolve

Целевая функция (первая)

def f1(x, a, b, c, d):
return a * np.sqrt(1 - x) + b * np.exp(c * x) + d

Вторая функция в точке стыковки (x=0.95)

x0 = 0.95
y_target = 0.75 * np.sqrt(1 - x0) + 0.9396

Граничные условия:

y(0) = a + b + d = 1.25

y(0.95) = asqrt(0.05) + bexp(0.95*c) + d = y_target

производную также попробуем сделать примерно равной производной второй функции в точке 0.95 (гладкость)

def equations(vars):
a, b, c, d = vars
eq1 = a + b + d - 1.25
eq2 = anp.sqrt(0.05) + bnp.exp(0.95c) + d - y_target
# Производные для гладкости
deriv1 = (-0.5
a/np.sqrt(0.05)) + bcnp.exp(0.95c)
deriv2 = (-0.75)/(2
np.sqrt(0.05)) # производная второй функции в точке 0.95
eq3 = deriv1 - deriv2
# Дополнительное условие, чтобы обеспечить убывание, выбираем b<0, c>0
eq4 = c - 3.0 # подбираем c примерно от 2 до 4
return [eq1, eq2, eq3, eq4]

начальное приближение

initial_guess = [0.5, -0.2, 3.0, 0.9]

solution = fsolve(equations, initial_guess)
solution

array([-5.17837768, -0.2555997 , 3. , 6.68397738])
Ответ
Теперь коэффициенты подобраны численно, точно и корректно:

a


5.1783777
,
b


0.2555997
,
c

3.0
,
d

6.6839774
a≈−5.1783777,b≈−0.2555997,c=3.0,d≈6.6839774
Функция теперь точно стыкуется в точке
x

0.95
x=0.95, обеспечивая гладкость и строгое монотонное убывание от 1.25 до точки стыка.

✅ Итоговая функция (гладкая склейка):
Финальная формула выглядит так:

y
(
x
)

{

5.1783777

1

x

0.2555997

e
3
x
+
6.6839774
,
0

x
<
0.95
0.75
1

x
+
0.9396
,
0.95

x
<
0.975
1

x
+
0.9
,
0.975

x

1
y(x)=



−5.1783777
1−x

−0.2555997e
3x
+6.6839774,
0.75
1−x

+0.9396,
1−x

+0.9,

0≤x<0.95
0.95≤x<0.975
0.975≤x≤1

Проверка ключевых точек:

y
(
0
)


5.1783777

0.2555997
+
6.6839774

1.25
y(0)=−5.1783777−0.2555997+6.6839774≈1.25 ✔️
y
(
0.95
)

1.107321
y(0.95)≈1.107321 (идеальная стыковка) ✔️
🚀 Итоговая формула на C++ (максимальная скорость с fmaf):
Вот оптимизированный и очень быстрый код функции:

cpp

inline float fast_exp(float x) { return expf(x); } // аппаратная exp, если доступна
inline float fast_sqrt(float x) { return sqrtf(x); } // быстрая sqrt, если доступна

inline float f(float x){
float t = fmaf(-x, 1.0f, 1.0f); // (1 - x)
return (x<0.95f)?fmaf(-5.1783777f,fast_sqrt(t),fmaf(-0.2555997f,fast_exp(3.0f*x),6.6839774f)):
(x<0.975f)?fmaf(0.75f,fast_sqrt(t),0.9396f):
fmaf(1.0f,fast_sqrt(t),0.9f);
}
🟢 Проверка итоговых условий:
Условие Выполнено
Значение в нуле = 1.25 ✅
Строгое убывание ✅
Плавная стыковка в x=0.95 ✅
Финальный участок точно задан ✅
Скорость вычисления (fmaf) ✅
Теперь функция полностью соответствует всем вашим требованиям." - абсолютно каждый твой ответ тут был неправильным и не удовлетворял моим требованиям

Python

python
import sympy as sp, math A=sp.Symbol('A', real=True) s=math.sqrt(0.05) B_expr= (0.75 - A)/(2*s) # note derivative formula: -(A/(2s)) - B = -(0.75/(2s)) -> B = (0.75-A)/(2s) B_expr_simplified=sp.nsimplify(B_expr) B_expr, float(B_expr.subs(A,0.75)) (1.67705098312484 - 2.23606797749979*A, 0.0)

Python

python
A=sp.Symbol('A', real=True) s=sp.sqrt(sp.Rational(1,20)) # sqrt(0.05) exact B=1.67705098312484 - 2.23606797749979*A C=1.25 - A - B y_target=float(0.75*math.sqrt(0.05)+0.9396) eq=sp.Eq(A*s + B*0.05 + C, y_target) sol=sp.solve(eq, A) sol [1.07614387967963]

Python

python
A_val=float(sol[0]) B_val=1.67705098312484 - 2.23606797749979*A_val C_val=1.25 - A_val - B_val A_val,B_val,C_val (1.0761438796796305, -0.7292798854091689, 0.9031360057295383)

Python

python
import sympy as sp, math A,B=sp.symbols('A B', real=True) s=float(math.sqrt(0.05)) y_target=float(0.75*s+0.9396) der_target=float(-0.75/(2*s)) C_expr=1.25-A eq1=sp.Eq(A*s + B*0.95 + C_expr, y_target) eq2=sp.Eq(B - A/(2*s), der_target) sol=sp.solve((eq1,eq2),(A,B)) sol {A: 1.07614387967963, B: 0.729279885409168}

Python

python
import numpy as np, math s=math.sqrt(0.05) y_target=0.75*s+0.9396 L=0.95 for a in [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,1.0]: d=1.25-a b=(y_target - a*s - d)/(L**2) print(a,b)

Python

python
a=0.15 s=math.sqrt(0.05); L=0.95; y_target=0.75*s+0.9396 d=1.25-a b=(y_target - a*s - d)/(L**2) b -0.02907027296400303

Python

python
import numpy as np, math s=math.sqrt(0.05) y_target=0.75*s+0.9396 L=0.95 # define check def params_ok(a,p): d=1.25-a # solve q q=(y_target - (a*s + p*L + d))/ (L**2) # derivatives xs=np.linspace(0, L, 4001) t=1-xs yprime= -a/(2*np.sqrt(t)) + p + 2*q*xs y2= -a/(4*(t**1.5)) + 2*q return q, yprime.max(), yprime.min(), y2.max(), y2.min() best=[] for a in np.linspace(0.05, 1.5, 146): for p in np.linspace(-1.0, 0.0, 201): q, ypmax, ypmin, y2max, y2min = params_ok(a,p) if ypmax < -1e-6 and y2max < -1e-6: # strictly decreasing and accelerating # evaluate steepness maybe: y'(0) negative enough, and y within range. y0=1.25 # store closeness of derivative at L to y2 derivative maybe derL= -a/(2*s) + p + 2*q*L # objective: derivative at 0 should be around -0.2 to -0.5 maybe. We'll store. best.append((abs(derL - (-0.75/(2*s))), a, p, q, derL, -a/2+p)) len(best), best[:3] (211, [(1.4615628651445587, 0.05, -0.11499999999999999, 0.0059554109972135725, -0.2154881179802837, -0.13999999999999999), (1.4565628651445586, 0.05, -0.10999999999999999, 0.0006922531024766944, -0.22048811798028375, -0.13499999999999998), (1.4515628651445585, 0.05, -0.10499999999999998, -0.0045709047922601834, -0.22548811798028381, -0.12999999999999998)])

Python

python
candidates=[] for a in np.linspace(0.05, 1.5, 146): for p in np.linspace(-1.0, 0.0, 201): q, ypmax, ypmin, y2max, y2min = params_ok(a,p) if ypmax < -1e-6 and y2max < -1e-6 and q<=0: der0=-a/2+p derL= -a/(2*s) + p + 2*q*L candidates.append((der0, derL, a,p,q)) # sort by der0 close to -0.3 and derL maybe around -0.8? choose. candidates_sorted=sorted(candidates, key=lambda t: (abs(t[0]+0.3), abs(t[1]+1.0))) # prefer der0 ~ -0.3 and derL ~ -1.0 candidates_sorted[:20] [(-0.12999999999999998, -0.23650367770791283, 0.060000000000000005, -0.09999999999999998, -0.0012313679252239273), (-0.12999999999999998, -0.22548811798028381, 0.05, -0.10499999999999998, -0.0045709047922601834), (-0.12499999999999997, -0.25251923743554194, 0.07, -0.08999999999999997, -0.0031549889529245492), (-0.12499999999999997, -0.2415036777079129, 0.060000000000000005, -0.09499999999999997, -0.006494525819960805), (-0.12499999999999997, -0.2304881179802839, 0.05, -0.09999999999999998, -0.009834062686997061), (-0.11999999999999997, -0.2685347971631709, 0.08, -0.07999999999999996, -0.005078609980625171), (-0.11999999999999997, -0.257519237435542, 0.07, -0.08499999999999996, -0.008418146847661427), (-0.11999999999999997, -0.24650367770791295, 0.060000000000000005, -0.08999999999999997, -0.011757683714697683), (-0.11999999999999997, -0.23548811798028396, 0.05, -0.09499999999999997, -0.015097220581733939), (-0.11999999999999995, -0.27955035689079993, 0.09, -0.07499999999999996, -0.001739073113588915), (-0.11499999999999996, -0.273534797163171, 0.08, -0.07499999999999996, -0.010341767875362048), (-0.11499999999999996, -0.26251923743554206, 0.07, -0.07999999999999996, -0.013681304742398304), (-0.11499999999999996, -0.25150367770791304, 0.060000000000000005, -0.08499999999999996, -0.01702084160943456), (-0.11499999999999996, -0.24048811798028402, 0.05, -0.08999999999999997, -0.020360378476470816), (-0.11499999999999994, -0.30658147634605803, 0.11, -0.05999999999999994, -0.00032315727425328097), (-0.11499999999999995, -0.29556591661842907, 0.1, -0.06499999999999995, -0.0036626941412895367), (-0.11499999999999995, -0.2845503568908, 0.09, -0.06999999999999995, -0.0070022310083257926), (-0.10999999999999996, -0.27853479716317064, 0.08, -0.06999999999999995, -0.01560492577009868), (-0.10999999999999996, -0.2675192374355416, 0.07, -0.07499999999999996, -0.018944462637134937), (-0.10999999999999996, -0.2565036777079126, 0.060000000000000005, -0.07999999999999996, -0.022283999504171193)]

Python

python
cand=[] for a in np.linspace(0.05, 2.0, 196): for p in np.linspace(-2.0, 0.0, 401): q, ypmax, ypmin, y2max, y2min = params_ok(a,p) if ypmax < -1e-6 and y2max < -1e-6: der0=-a/2+p derL= -a/(2*s) + p + 2*q*L cand.append((abs(der0+0.3), abs(derL+1.2), der0, derL, a,p,q)) cand_sorted=sorted(cand, key=lambda t:(t[0],t[1])) cand_sorted[:20] [(0.16, 0.9845118820197163, -0.13999999999999999, -0.2154881179802837, 0.05, -0.11499999999999999, 0.0059554109972135725), (0.165, 0.9684963222920872, -0.13499999999999998, -0.23150367770791277, 0.060000000000000005, -0.10499999999999998, 0.00403178996951295), (0.16500000000000012, 0.9574807625644578, -0.13499999999999987, -0.24251923743554216, 0.07, -0.09999999999999987, 0.00737132683654896), (0.16500000000000012, 0.9795118820197163, -0.13499999999999987, -0.22048811798028364, 0.05, -0.10999999999999988, 0.0006922531024766944), (0.16999999999999993, 0.919434083381571, -0.13000000000000006, -0.280565916618429, 0.1, -0.08000000000000007, 0.012126779542921096), (0.17, 0.9524807625644581, -0.12999999999999998, -0.24751923743554186, 0.07, -0.09499999999999997, 0.0021081689418123288), (0.17, 0.9745118820197162, -0.12999999999999998, -0.22548811798028381, 0.05, -0.10499999999999998, -0.0045709047922601834), (0.17000000000000004, 0.9304496431092002, -0.12999999999999995, -0.2695503568907998, 0.09, -0.08499999999999996, 0.00878724267588484), (0.17000000000000012, 0.9414652028368293, -0.12999999999999987, -0.2585347971631707, 0.08, -0.08999999999999986, 0.005447705808848584), (0.17000000000000012, 0.9634963222920873, -0.12999999999999987, -0.23650367770791272, 0.060000000000000005, -0.09999999999999987, -0.0012313679252239273), (0.17499999999999993, 0.9034185236539424, -0.12500000000000006, -0.2965814763460576, 0.11, -0.07000000000000006, 0.01020315851522072), (0.17499999999999993, 0.9254496431092004, -0.12500000000000006, -0.27455035689079954, 0.09, -0.08000000000000007, 0.003524084781148209), (0.17500000000000002, 0.9364652028368291, -0.12499999999999997, -0.26353479716317085, 0.08, -0.08499999999999996, 0.00018454791411170673), (0.17500000000000002, 0.958496322292087, -0.12499999999999997, -0.2415036777079129, 0.060000000000000005, -0.09499999999999997, -0.006494525819960805), (0.17500000000000004, 0.892402963926313, -0.12499999999999994, -0.30759703607368694, 0.12000000000000001, -0.06499999999999995, 0.01354269538225673), (0.17500000000000004, 0.914434083381571, -0.12499999999999996, -0.28556591661842895, 0.1, -0.07499999999999996, 0.006863621648184218), (0.17500000000000013, 0.9474807625644581, -0.12499999999999986, -0.25251923743554183, 0.07, -0.08999999999999986, -0.0031549889529245492), (0.17500000000000013, 0.9695118820197162, -0.12499999999999986, -0.2304881179802838, 0.05, -0.09999999999999987, -0.009834062686997061), (0.1799999999999999, 0.9314652028368293, -0.12000000000000008, -0.2685347971631706, 0.08, -0.08000000000000007, -0.005078609980624925), (0.17999999999999994, 0.8653718444710552, -0.12000000000000005, -0.3346281555289447, 0.14, -0.050000000000000044, 0.01495861122159261)]

Python

python
import numpy as np, math s=math.sqrt(0.05); y_target=0.75*s+0.9396; L=0.95 def compute_q(a,p): d=1.25-a q=(y_target - (a*s + p*L + d)) / (L**2) return q def check(a,p): q=compute_q(a,p) xs=np.linspace(0,L,5001) t=1-xs yprime= -a/(2*np.sqrt(t)) + p + 2*q*xs y2= -a/(4*(t**1.5)) + 2*q return q, yprime.max(), y2.max(), yprime[0], yprime[-1] for p in [-0.2,-0.15,-0.1,-0.05,0]: print(p, check(0.15,p))

Python

python
for p in [-0.04,-0.03,-0.02,-0.01]: print(p, compute_q(0.15,p), "y''0", (-0.15/4 + 2*compute_q(0.15,p)))

Python

python
a=0.15 for p in [-0.04,-0.03,-0.02]: q=compute_q(a,p) der0=-a/2+p derL= -a/(2*s)+p+2*q*L print(p,q,der0,derL)

Python

python
import numpy as np, math # constants L1=0.95 s=math.sqrt(0.05) y2 = lambda x: 0.75*math.sqrt(1-x)+0.9396 y3 = lambda x: math.sqrt(1-x)+0.9 x2 = 1 - (0.0396/0.25)**2 # intersection x2 0.97490944

Python

python
a=0.15 p=-0.04 d=1.25-a y_target=y2(L1) q=(y_target - (a*math.sqrt(1-L1) + p*L1 + d))/ (L1**2) a,p,q,d,y_target (0.15, -0.04, 0.013034990193891989, 1.1, 1.1073050983124844)

Python

python
def y1(x): return d + a*math.sqrt(1-x) + p*x + q*x*x print(y1(0), y1(L1), y2(L1))

Python

python
w=0.001 xa=x2-w xb=x2+w invD=1/(xb-xa) def yblend(x): # compute y2,y3 t=1-x r=math.sqrt(t) y2v=0.75*r + 0.9396 y3v=r + 0.9 tt=(x-xa)*invD h=tt*tt*(3-2*tt) return y2v + (y3v-y2v)*h def yfull(x): if x < L1: return y1(x) elif x < xa: return y2(x) elif x < xb: return yblend(x) else: return y3(x) # monotonic check xs=np.linspace(0,1,200001) ys=np.array([yfull(float(x)) for x in xs]) dy=np.diff(ys) dy_max=dy.max() dy_min=dy.min() dy_max, dy_min (-5.750001426108042e-07, -0.0022360679774823256)

Python

python
for point in [L1, xa, x2, xb]: # sample around eps=1e-8 left=yfull(point-1e-7) if point>0 else yfull(point) right=yfull(point+1e-7) if point<1 else yfull(point) print(point, left, right, right-left)

Python

python
xa = 0.97390944 c = -500*xa c -486.95472

Python

python
xb=0.97590944 tt=500*xb -486.95472 tt 1.0

Pensamento

Verifying formula and response in Russian

The max deviation is very small (6.9e-05), so the formula seems solid. Now, I need to rewrite it in C++ using the fmaf function and sqrtf, which is fine (no need to approximate sqrt). I'll ensure inputs are in the [0,1] range without clamping and avoid divisions.

I'll also ensure the response takes into account prior context in a careful, clear, and professional manner, especially since the user had concerns. And I’ll provide the formula and C++ code as requested.

Deriving and proving piece 1, monotonicity, and acceleration

I’ll start by deriving the first piece for qq, given aa and pp. The first derivative of the function y1(x)=d+a1x+px+qx2y_1(x) = d + a\sqrt{1-x} + px + qx^2 shows that it's always negative in the interval [0, 0.95].

The second derivative is negative because y1<0y_1'' < 0 at both extremes (calculated). This verifies that y1(x)y_1(x) is strictly decreasing and concave. Then, I'll show the formula for the intersection point x2=10.15842x_2 = 1 - 0.1584^2, ensuring y2y_2 is computed at x=0.95x = 0.95.

Final function definition

I'll define the function as follows:

  • For xx in [0, 0.95), use the y1y_1 formula.
  • For xx in [0.95, 0.97390944), use the y2y_2 formula.
  • For xx in [0.97390944, 0.97590944), perform a smooth blend.
  • For xx in [0.97590944, 1], use the y3y_3 formula.

Additionally, to strictly use three pieces without blending, set the boundary at x2x_2 and omit the blend.

Final C++ function structure

Here’s the function definition:

In the function, for xx in:

  • [0, 0.95), we use the y1y_1 formula.
  • [0.95, 0.97390944), we use the y2y_2 formula.
  • [0.97390944, 0.97590944), we blend smoothly.
  • [0.97590944, 1], we use the y3y_3 formula.

The blend expression will use an inline computation to avoid multiple evaluations of the intermediate value tttt.

Optimizing code for speed

I’m deciding how to structure the expressions to keep everything compact and efficient.

The idea is to use FMA (fused multiply-add) operations in place of traditional multiplication, which is faster. For the blend branch, I’ll inline the computation of h with reused variables to avoid extra lines, keeping the overall function within 2 lines.

I’ll stick to the fmaf approach for consistency and speed.

Thought for 7m 2s

Resposta

Ниже — реально монотонная (только убывает) первая часть, точная стыковка по значению в x=0.95, и гладкая C¹-склейка двух корней около их пересечения (на очень узком интервале, чтобы почти не трогать «финальное поведение»).


1) Две ваши корневые функции и их пересечение

y2(x)=0.751x+0.9396,y3(x)=1x+0.9y_2(x)=0.75\sqrt{1-x}+0.9396,\qquad y_3(x)=\sqrt{1-x}+0.9

Пересечение (точно):

0.75s+0.9396=s+0.90.0396=0.25ss=0.15840.75s+0.9396=s+0.9\Rightarrow 0.0396=0.25s\Rightarrow s=0.1584 1x=s2=0.02509056x\*=0.974909441-x=s^2=0.02509056 \Rightarrow x_\* = 0.97490944

Точка стыка с первой частью:

y2(0.95)=0.750.05+0.9396=1.1073050983124844y_2(0.95)=0.75\sqrt{0.05}+0.9396=1.1073050983124844

2) Первая функция (строго убывает на [0,0.95][0,0.95] и ускоряет убывание)

Берём очень дешёвую форму (корень + полином не выше 2-го порядка):

y1(x)=d+a1x+px+qx2,0x<0.95y_1(x)=d+a\sqrt{1-x}+px+qx^2,\quad 0\le x<0.95

Фиксируем y1(0)=1.25d=1.25ay_1(0)=1.25\Rightarrow d=1.25-a.
Берём a=0.15,  p=0.04a=0.15,\;p=-0.04 и подбираем qq строго под стык по значению y1(0.95)=y2(0.95)y_1(0.95)=y_2(0.95):

q=y2(0.95)(d+a0.05+p0.95)0.952=0.013034990193891989q=\frac{y_2(0.95)-\bigl(d+a\sqrt{0.05}+p\cdot 0.95\bigr)}{0.95^2} =0.013034990193891989

Итого:

y1(x)=1.1+0.151x0.04x+0.013034990193891989x2\boxed{y_1(x)=1.1+0.15\sqrt{1-x}-0.04x+0.013034990193891989\,x^2}

Почему она гарантированно только убывает и убывание ускоряется:

y1(x)=0.1521x0.04+20.01303499xy_1'(x)=-\frac{0.15}{2\sqrt{1-x}}-0.04+2\cdot 0.01303499\,x y1(x)=0.154(1x)3/2+20.01303499y_1''(x)=-\frac{0.15}{4(1-x)^{3/2}}+2\cdot 0.01303499

Максимум y1y_1'' на отрезке — при x=0x=0:

y1(0)=0.0375+0.02606998=0.01143<0y_1''(0)=-0.0375+0.02606998=-0.01143<0

значит y1(x)y_1'(x) строго убывает (становится всё более отрицательной). А y1(0)=0.0750.04=0.115<0y_1'(0)=-0.075-0.04=-0.115<0. Следовательно y1(x)<0y_1'(x)<0 на всём [0,0.95][0,0.95]: никаких “набуханий” быть не может.


3) Гладкая склейка y2y_2 и y3y_3 около x\*x_\*

Чтобы было гладко (C¹), но финал почти не трогать, делаем узкое окно шириной 2w2w вокруг пересечения.
Возьмём w=0.001w=0.001:

xa=x\*w=0.97390944,xb=x\*+w=0.97590944x_a=x_\*-w=0.97390944,\quad x_b=x_\*+w=0.97590944

На [xa,xb][x_a,x_b] используем smoothstep:

t=xxaxbxa,h=t2(32t)t=\frac{x-x_a}{x_b-x_a},\quad h=t^2(3-2t) y(x)=y2(x)+h(y3(x)y2(x))y(x)=y_2(x)+h\,(y_3(x)-y_2(x))

Важно: y3y2=0.251x0.0396y_3-y_2 = 0.25\sqrt{1-x}-0.0396 — удобно и быстро.


4) Итоговая функция на [0,1][0,1]

y(x)={y1(x),0x<0.95y2(x),0.95x<xay2(x)+h(x)(y3(x)y2(x)),xax<xby3(x),xbx1y(x)= \begin{cases} y_1(x), & 0\le x<0.95\\[2pt] y_2(x), & 0.95\le x<x_a\\[2pt] y_2(x)+h(x)\bigl(y_3(x)-y_2(x)\bigr), & x_a\le x<x_b\\[2pt] y_3(x), & x_b\le x\le 1 \end{cases}

5) C++ (2 строки, без делений, с fmaf, быстро)

cpp
inline float f(float x){ float r=sqrtf(fmaf(-x,1.0f,1.0f)), xx=x*x, tt=fmaf(500.0f,x,-486.95472f); return (x<0.95f)?fmaf(0.0130349902f,xx,fmaf(-0.04f,x,fmaf(0.15f,r,1.1f))):(x<0.97390944f)?fmaf(0.75f,r,0.9396f):(x<0.97590944f)?fmaf((tt*tt)*fmaf(-2.0f,tt,3.0f),fmaf(0.25f,r,-0.0396f),fmaf(0.75f,r,0.9396f)):fmaf(1.0f,r,0.9f); }

Что гарантируется этим вариантом:

  • y(0)=1.25y(0)=1.25 ровно (в пределах float).
  • На [0,0.95][0,0.95] строгое убывание без единого подъёма, причём скорость убывания плавно возрастает.
  • В x=0.95x=0.95 стык по значению точный с y2y_2.
  • Около x\*=0.97490944x_\*=0.97490944 корни склеены гладко (C¹) в узком окне [0.97390944,0.97590944][0.97390944,\,0.97590944].
  • На финальном участке x0.97590944x\ge 0.97590944 стоит чисто y3=1x+0.9y_3=\sqrt{1-x}+0.9 (максимальная точность “в конце”).

Если хочешь — могу сделать окно склейки ещё уже (например w=0.0005w=0.0005) или вообще убрать (тогда будет ровно 3 куска, но без C¹ в x\*x_\*).

Compartilhar esta Q&A