diff --git a/frontend/textbooks/algebra_11_ch2.html b/frontend/textbooks/algebra_11_ch2.html index 791fbd1..302b6f9 100644 --- a/frontend/textbooks/algebra_11_ch2.html +++ b/frontend/textbooks/algebra_11_ch2.html @@ -598,15 +598,445 @@ function wireReadBtn(paraId){ function buildP4(){ const box = document.getElementById('p4-body'); let html = ''; - html += makeCard('theory', 'В разработке', '4.0', ` -

Содержание параграфа Показательная функция будет добавлено в Phase 1+.

-

Раздел Phase 0 — skeleton. Здесь появятся теория, примеры и интерактивы.

-

Ключевая формула: $y = a^x$

- `); + + /* === ТЕОРИЯ === */ + + html += makeCard('theory', 'Определение и графики', '4.1', ` +

Функция вида $y = a^x$, где $a > 0$, $a \\ne 1$, называется показательной.

+

Условия $a > 0$ и $a \\ne 1$ — обязательны:

+ +

Примеры показательных функций:

+ +

График показательной функции:

+ +

Все графики $y = a^x$ проходят через точку $(0; 1)$ — ведь $a^0 = 1$ при любом $a > 0$.

`); + + html += makeCard('rule', 'Свойства показательной функции', '4.2', ` +

Сведём все ключевые свойства функции $y = a^x$ в одну таблицу — отдельно для двух случаев $a > 1$ и $0 < a < 1$:

+
+ + + + + + + + + + + + + + + + + + + +
Свойство$a > 1$$0 < a < 1$
$D(y)$ — область определения$\\mathbb{R}$ — вся числовая прямая
$E(y)$ — область значений$(0; +\\infty)$ — только положительные значения
Нулинет — график не пересекает ось $Ox$
Знакопостоянство$y > 0$ при всех $x \\in \\mathbb{R}$
Монотонностьвозрастает на $\\mathbb{R}$убывает на $\\mathbb{R}$
Точка пересечения с $Oy$$(0; 1)$
Асимптота$y = 0$ (ось абсцисс)
При $x \\to +\\infty$$y \\to +\\infty$$y \\to 0$
При $x \\to -\\infty$$y \\to 0$$y \\to +\\infty$
+
+

Ключевая идея: два графика $y = a^x$ и $y = (1/a)^x$ симметричны относительно оси $Oy$, потому что $(1/a)^x = a^{-x}$.

`); + + html += makeCard('example', 'Показательная функция в реальной жизни', '4.3', ` +

Показательная функция описывает огромное число процессов в природе и технике — везде, где скорость изменения пропорциональна текущему значению величины.

+ +

Поэтому изучение функции $y = a^x$ — это фундамент для физики, химии, биологии, экономики и информатики.

`); + + /* === ИНТЕРАКТИВЫ === */ + + /* IV1 — двухпанельный сравнительный визуализатор */ + html += `
+
ИНТЕРАКТИВ 1
Двухпанельный визуализатор $y = a^x$
+
Меняй основание $a$ ползунком — наблюдай, как меняется характер функции. Левая панель для $a > 1$ (возрастающая, синий), правая — для $0 < a < 1$ (убывающая, оранжевый). Snap-точки: $a \\in \\{0{,}1;\\ 0{,}25;\\ 0{,}5;\\ 1;\\ 2;\\ e;\\ 3;\\ 10\\}$. Точка $(0; 1)$ — всегда на графике.
+
+ +
+
+
+
`; + + /* IV2 — калькулятор y = a^x */ + html += `
+
ИНТЕРАКТИВ 2
Калькулятор $y = a^x$
+
Введи основание $a$ ($a > 0$, $a \\ne 1$) и показатель $x$. Получи $y = a^x$ и характер функции. Под результатом — мини-график с отметкой точки $(x; y)$.
+
+ $a$ = + + $x$ = + + +
+
+ Быстро для $a$: + + + + +
+
+
+
+
`; + + /* IV3 — квикфайр «Возрастает или убывает?» */ + html += `
+
ИНТЕРАКТИВ 3
Возрастает или убывает?
+
Для каждой показательной функции определи характер монотонности. 8 заданий.
+
Задача 1 / 8Очки: 0 / 8
+
+
+
+
+
`; + + /* IV4 — тренажёр значений */ + html += `
+
ИНТЕРАКТИВ 4
Тренажёр значений $a^x$
+
Вычисли значение $a^x$ (десятичное до 4 знаков, допуск $\\pm 0{,}05$). 6 задач.
+
Задача 1 / 6Очки: 0 / 6
+
+
+ ответ = + + + +
+
+
`; + html += secNavFor('p4'); html += readButton('p4'); + box.innerHTML = html; renderMath(box); + + /* === IV1 — двухпанельный визуализатор === */ + (function(){ + const sa = document.getElementById('p4-iv1-sa'); + const aL = document.getElementById('p4-iv1-a'); + const svg = document.getElementById('p4-iv1-svg'); + const desc = document.getElementById('p4-iv1-desc'); + const E = Math.E; + const SNAP = [0.1, 0.25, 0.5, 1, 2, E, 3, 10]; + const seen = new Set(); + let _done = false; + + const COL_GROW = '#2563eb'; + const COL_DECAY = '#ea580c'; + const COL_NEUTRAL = '#94a3b8'; + const COL_POINT01 = '#10b981'; + const COL_POINT1A = '#dc2626'; + + function aLabel(a){ + if(Math.abs(a - E) < 0.03) return 'e \\approx 2{,}72'; + if(Math.abs(a - Math.round(a)) < 0.005) return String(Math.round(a)); + if(Math.abs(a - 0.5) < 0.005) return '\\tfrac{1}{2}'; + if(Math.abs(a - 0.25) < 0.005) return '\\tfrac{1}{4}'; + if(Math.abs(a - 0.1) < 0.005) return '\\tfrac{1}{10}'; + return (+a.toFixed(2)).toString(); + } + function aPlain(a){ + if(Math.abs(a - E) < 0.03) return 'e'; + return (+a.toFixed(2)).toString(); + } + function drawPanel(active, isGrow, a, ox){ + const W = 360, H = 360, pad = 28; + const xmin = -3, xmax = 3, ymin = -0.5, ymax = 8; + const ax = axes2D(W, H, pad, xmin, xmax, ymin, ymax); + // сдвинуть всё содержимое axes2D на ox по X через обёртку + let g = ''; + g += ax.content; + const col = isGrow ? COL_GROW : COL_DECAY; + const title = isGrow ? 'a > 1 \\u2014 возрастающая' : '0 < a < 1 \\u2014 убывающая'; + // заголовок панели + g += '' + + (isGrow ? 'a > 1 — возрастающая' : '0 < a < 1 — убывающая') + + ''; + + if(!active){ + // серая заглушка + g += ''; + g += ''; + g += isGrow ? 'выбери a > 1' : 'выбери 0 < a < 1'; + g += ''; + g += ''; + return g; + } + + // асимптота y = 0 + g += asymptote('h', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#94a3b8'); + + // график y = a^x + g += plotFunc(x => Math.pow(a, x), xmin, xmax, ax.toX, ax.toY, col, 240); + + // точка (0; 1) — зелёная + g += pointWithDrop(0, 1, ax.toX, ax.toY, COL_POINT01, '(0; 1)'); + // точка (1; a) — красная (если a в пределах ymax) + if(a > 0 && a <= ymax){ + g += pointWithDrop(1, a, ax.toX, ax.toY, COL_POINT1A, '(1; '+aPlain(a)+')'); + } + g += ''; + return g; + } + function drawConstPanel(a){ + // обе панели показывают серую линию y = 1 + const W = 720, H = 360, pad = 28; + const xmin = -3, xmax = 3, ymin = -0.5, ymax = 8; + let out = ''; + // левая + let leftAx; + { + const ax = axes2D(360, H, pad, xmin, xmax, ymin, ymax); + leftAx = ax; + let g = '' + ax.content; + g += 'a = 1: y = 1 константа'; + g += plotFunc(x => 1, xmin, xmax, ax.toX, ax.toY, COL_NEUTRAL, 60); + g += pointWithDrop(0, 1, ax.toX, ax.toY, COL_POINT01, '(0; 1)'); + g += ''; + out += g; + } + // правая + { + const ax = axes2D(360, H, pad, xmin, xmax, ymin, ymax); + let g = '' + ax.content; + g += 'a = 1 — не показательная'; + g += plotFunc(x => 1, xmin, xmax, ax.toX, ax.toY, COL_NEUTRAL, 60); + g += pointWithDrop(0, 1, ax.toX, ax.toY, COL_POINT01, '(0; 1)'); + g += ''; + out += g; + } + return out; + } + + function describe(a){ + if(Math.abs(a - 1) < 0.01){ + return '$a = 1$: функция $y = 1^x = 1$ — постоянная, не является показательной (нарушено условие $a \\ne 1$).'; + } + if(a > 1){ + const aTxt = aLabel(a); + return '$a = '+aTxt+' > 1$ — функция возрастает.
' + + '$D = \\mathbb{R}$, $E = (0; +\\infty)$. Проходит через $(0; 1)$ и $(1; '+aPlain(a)+')$.
' + + 'При $x \\to +\\infty$: $y \\to +\\infty$. При $x \\to -\\infty$: $y \\to 0$ (асимптота $y = 0$).'; + } + // 0 < a < 1 + const aTxt = aLabel(a); + return '$a = '+aTxt+'$, $0 < a < 1$ — функция убывает.
' + + '$D = \\mathbb{R}$, $E = (0; +\\infty)$. Проходит через $(0; 1)$ и $(1; '+aPlain(a)+')$.
' + + 'При $x \\to +\\infty$: $y \\to 0$ (асимптота $y = 0$). При $x \\to -\\infty$: $y \\to +\\infty$.'; + } + + function draw(){ + let a = +sa.value; + a = snapToValue(a, SNAP, 0.06); + aL.textContent = aLabel(a); + + let svgContent = ''; + if(Math.abs(a - 1) < 0.01){ + svgContent = drawConstPanel(a); + } else if(a > 1){ + svgContent += drawPanel(true, true, a, 0); + svgContent += drawPanel(false, false, a, 360); + } else { + svgContent += drawPanel(false, true, a, 0); + svgContent += drawPanel(true, false, a, 360); + } + // разделительная линия между панелями + svgContent += ''; + + svg.innerHTML = svgContent; + desc.innerHTML = describe(a); + renderMath(desc); + + const key = (+a.toFixed(2)).toString(); + seen.add(key); + if(!_done && seen.size >= 6){ _done = true; addXp(10, 'p4-iv1'); bumpProgress('p4', 15); } + } + sa.addEventListener('input', draw); + draw(); + })(); + + /* === IV2 — калькулятор y = a^x === */ + (function(){ + const aI = document.getElementById('p4-iv2-a'); + const xI = document.getElementById('p4-iv2-x'); + const go = document.getElementById('p4-iv2-go'); + const out = document.getElementById('p4-iv2-out'); + const fb = document.getElementById('p4-iv2-fb'); + const mini = document.getElementById('p4-iv2-mini'); + const used = new Set(); + let _done = false; + + document.querySelectorAll('#p4-iv2 [data-qa]').forEach(b => { + b.addEventListener('click', () => { aI.value = b.dataset.qa; calc(); }); + }); + + function drawMini(a, xPt){ + const W = 240, H = 140, pad = 16; + const xmin = -3, xmax = 3; + // подобрать ymax + let ymax = Math.max(1.5, Math.pow(a, 2), Math.pow(a, -2)); + ymax = Math.min(ymax, 10); + const ymin = -0.3; + const ax = axes2D(W, H, pad, xmin, xmax, ymin, ymax); + let g = ax.content; + const col = a > 1 ? '#2563eb' : '#ea580c'; + g += asymptote('h', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#94a3b8'); + g += plotFunc(x => Math.pow(a, x), xmin, xmax, ax.toX, ax.toY, col, 160); + g += pointWithDrop(0, 1, ax.toX, ax.toY, '#10b981', ''); + // точка (x; a^x), если в пределах + const yPt = Math.pow(a, xPt); + if(xPt >= xmin && xPt <= xmax && yPt >= ymin && yPt <= ymax){ + g += pointWithDrop(xPt, yPt, ax.toX, ax.toY, '#dc2626', ''); + } + mini.innerHTML = g; + } + + function calc(){ + const a = parseFloat(aI.value); + const x = parseFloat(xI.value); + if(!isFinite(a) || !isFinite(x)){ feedback(fb, false, '✗ Введи числа $a$ и $x$.'); return; } + if(a <= 0){ feedback(fb, false, '✗ Основание должно быть положительным: $a > 0$.'); return; } + if(Math.abs(a - 1) < 1e-9){ feedback(fb, false, '✗ При $a = 1$ функция не является показательной.'); return; } + const y = Math.pow(a, x); + const sign = a > 1 ? 'возрастающая' : 'убывающая'; + const signCol = a > 1 ? '#1d4ed8' : '#c2410c'; + out.innerHTML = '
$y = '+aI.value+'^{'+xI.value+'} = '+(+y.toFixed(4))+'$
' + + '
Функция $y = '+aI.value+'^x$ — '+sign+'
'; + renderMath(out); + feedback(fb, true, '✓ Готово.'); + drawMini(a, x); + const key = (+a.toFixed(3))+'_'+(+x.toFixed(3)); + used.add(key); + if(!_done && used.size >= 4){ _done = true; addXp(10, 'p4-iv2'); bumpProgress('p4', 15); } + } + go.addEventListener('click', calc); + aI.addEventListener('keydown', e => { if(e.key === 'Enter') calc(); }); + xI.addEventListener('keydown', e => { if(e.key === 'Enter') calc(); }); + calc(); + })(); + + /* === IV3 — квикфайр «Возрастает или убывает?» === */ + (function(){ + const Q = [ + { q:'$y = 2^x$', ans:0 }, + { q:'$y = (0{,}5)^x$', ans:1 }, + { q:'$y = \\left(\\dfrac{1}{3}\\right)^x$', ans:1 }, + { q:'$y = 10^x$', ans:0 }, + { q:'$y = e^x$', ans:0 }, + { q:'$y = \\left(\\dfrac{1}{2}\\right)^x$', ans:1 }, + { q:'$y = 5^x$', ans:0 }, + { q:'$y = (0{,}1)^x$', ans:1 }, + ]; + const OPTS = ['Возрастает', 'Убывает']; + let i = 0, score = 0; + const qEl = document.getElementById('p4-iv3-q'); + const oEl = document.getElementById('p4-iv3-opts'); + const fb = document.getElementById('p4-iv3-fb'); + const iEl = document.getElementById('p4-iv3-i'); + const sEl = document.getElementById('p4-iv3-s'); + + function show(){ + if(i >= Q.length){ + qEl.innerHTML = 'Готово! Результат: '+score+' / '+Q.length; + oEl.innerHTML = ''; + if(score === Q.length){ addXp(15, 'p4-iv3'); bumpProgress('p4', 25); } + else if(score >= 5){ addXp(8, 'p4-iv3'); bumpProgress('p4', 15); } + return; + } + iEl.textContent = (i+1); + sEl.textContent = score; + const item = Q[i]; + qEl.innerHTML = 'Какая монотонность у функции ' + item.q + ' ?'; + oEl.innerHTML = OPTS.map((o, k) => '').join(''); + fb.style.display = 'none'; + renderMath(qEl); + oEl.querySelectorAll('button').forEach(b => { + b.addEventListener('click', () => { + const k = +b.dataset.k; + if(k === item.ans){ score++; feedback(fb, true, '✓ Верно! Дальше ▶'); } + else feedback(fb, false, '✗ Неверно. Правильно: '+OPTS[item.ans]+'. Дальше ▶'); + sEl.textContent = score; + oEl.querySelectorAll('button').forEach(x => x.disabled = true); + i++; + setTimeout(show, 1100); + }); + }); + } + document.getElementById('p4-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); }); + show(); + })(); + + /* === IV4 — тренажёр значений === */ + (function(){ + const Q = [ + { q:'$2^3 = \\,?$', ans:8, hint:'$2 \\cdot 2 \\cdot 2 = 8$' }, + { q:'$3^{-2} = \\,?$ (десятичная)', ans:1/9, hint:'$3^{-2} = \\dfrac{1}{9} \\approx 0{,}1111$' }, + { q:'$\\left(\\dfrac{1}{2}\\right)^4 = \\,?$ (десятичная)', ans:0.0625, hint:'$\\dfrac{1}{16} = 0{,}0625$' }, + { q:'$10^{1{,}5} = \\,?$', ans:31.6228, hint:'$10^{3/2} = 10\\sqrt{10} \\approx 31{,}62$' }, + { q:'$4^{0{,}5} = \\,?$', ans:2, hint:'$4^{1/2} = \\sqrt{4} = 2$' }, + { q:'$9^{1{,}5} = \\,?$', ans:27, hint:'$9^{3/2} = (\\sqrt{9})^3 = 3^3 = 27$' }, + ]; + let i = 0, score = 0; + function show(){ + const qEl = document.getElementById('p4-iv4-q'); + const iEl = document.getElementById('p4-iv4-i'); + const sEl = document.getElementById('p4-iv4-s'); + const fb = document.getElementById('p4-iv4-fb'); + const ansI = document.getElementById('p4-iv4-ans'); + if(i >= Q.length){ + qEl.innerHTML = 'Готово! Результат: '+score+' / '+Q.length; + if(score === Q.length){ addXp(15, 'p4-iv4'); bumpProgress('p4', 25); } + else if(score >= 4){ addXp(8, 'p4-iv4'); bumpProgress('p4', 15); } + return; + } + iEl.textContent = (i+1); + sEl.textContent = score; + qEl.innerHTML = Q[i].q; + ansI.value = ''; + renderMath(qEl); + fb.style.display = 'none'; + } + function go(){ + if(i >= Q.length) return; + const fb = document.getElementById('p4-iv4-fb'); + const raw = document.getElementById('p4-iv4-ans').value.replace(',', '.'); + const ans = parseFloat(raw); + if(isNaN(ans)){ feedback(fb, false, '✗ Введи число.'); return; } + // допуск: либо абсолютный 0.05, либо относительный 1% для крупных значений + const tol = Math.max(0.05, Math.abs(Q[i].ans) * 0.01); + if(Math.abs(ans - Q[i].ans) < tol){ + score++; + feedback(fb, true, '✓ Верно! '+Q[i].hint+'. Дальше ▶'); + } else { + feedback(fb, false, '✗ Неверно. Ответ $\\approx '+(+Q[i].ans.toFixed(4))+'$ ('+Q[i].hint+'). Дальше ▶'); + } + document.getElementById('p4-iv4-s').textContent = score; + i++; + setTimeout(show, 1400); + } + document.getElementById('p4-iv4-go').addEventListener('click', go); + document.getElementById('p4-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); }); + document.getElementById('p4-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); }); + show(); + })(); + wireReadBtn('p4'); }