From 566197df48149bb7e9f6c39bc73d02fc463ee045 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 29 May 2026 11:45:24 +0300 Subject: [PATCH] =?UTF-8?q?feat(alg11=20ch1=20wave2):=20=C2=A72=20=C2=AB?= =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B5=D0=BD=D0=BD=D0=B0=D1=8F=20=D1=84?= =?UTF-8?q?=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F=20y=20=3D=20x^=CE=B1=C2=BB?= =?UTF-8?q?=20+=20=D0=B3=D0=BB=D0=B0=D0=B2=D0=BD=D1=8B=D0=B9=20=D0=B2?= =?UTF-8?q?=D0=B8=D0=B7=D1=83=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=82=D0=BE?= =?UTF-8?q?=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/textbooks/algebra_11_ch1.html | 449 ++++++++++++++++++++++++- 1 file changed, 443 insertions(+), 6 deletions(-) diff --git a/frontend/textbooks/algebra_11_ch1.html b/frontend/textbooks/algebra_11_ch1.html index d9486a5..328721d 100644 --- a/frontend/textbooks/algebra_11_ch1.html +++ b/frontend/textbooks/algebra_11_ch1.html @@ -923,15 +923,452 @@ function buildP1(){ function buildP2(){ const box = document.getElementById('p2-body'); let html = ''; - html += makeCard('theory', 'В разработке', '2.0', ` -

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

-

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

-

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

- `); - html += secNavFor('p2'); + + html += makeCard('theory', 'Определение и классификация', '2.1', ` +

Функция вида $y = x^\\alpha$, где $\\alpha$ — действительное число, называется степенной функцией.

+

Свойства функции $y = x^\\alpha$ (область определения, чётность, монотонность, вид графика) полностью зависят от того, какое число $\\alpha$:

+ +

Несмотря на это разнообразие, все графики $y = x^\\alpha$ при $x = 1$ проходят через одну и ту же точку $(1; 1)$.

`); + + html += makeCard('rule', 'Шесть ключевых случаев — свойства', '2.2', ` +

Перед нами таблица свойств $y = x^\\alpha$ для самых важных значений $\\alpha$.

+
+ + + + + + + + + + + + + + + + + + + +
Случай$D$$E$ЧётностьМонотонность
$\\alpha = 2k$ (нат. чёт.)$\\mathbb{R}$$[0; +\\infty)$чётная$\\searrow$ на $(-\\infty; 0]$, $\\nearrow$ на $[0; +\\infty)$
$\\alpha = 2k+1$ (нат. неч.)$\\mathbb{R}$$\\mathbb{R}$нечётная$\\nearrow$ на $\\mathbb{R}$
$\\alpha = -2k$ (целое отр. чёт.)$\\mathbb{R} \\setminus \\{0\\}$$(0; +\\infty)$чётная$\\nearrow$ на $(-\\infty; 0)$, $\\searrow$ на $(0; +\\infty)$
$\\alpha = -(2k+1)$ (целое отр. неч.)$\\mathbb{R} \\setminus \\{0\\}$$\\mathbb{R} \\setminus \\{0\\}$нечётная$\\searrow$ на каждом промежутке
$\\alpha = 1/n$ ($n$ чёт.)$[0; +\\infty)$$[0; +\\infty)$$\\nearrow$
$\\alpha = 1/n$ ($n$ неч.)$\\mathbb{R}$$\\mathbb{R}$нечётная$\\nearrow$
$\\alpha$ иррац.$(0; +\\infty)$$(0; +\\infty)$$\\nearrow$ при $\\alpha > 0$; $\\searrow$ при $\\alpha < 0$
+
+

Все графики проходят через $(1; 1)$: ведь $1^\\alpha = 1$ при любом $\\alpha$.

`); + + html += makeCard('example', 'Степенные функции в природе и физике', '2.3', ` +

Степенная функция $y = x^\\alpha$ — не абстракция: она описывает множество законов природы и формул из геометрии.

+ +

Знание свойств степенной функции — это универсальный инструмент для понимания физики, химии, биологии и техники.

`); + + /* INTERACTIVE 1 — главный визуализатор y = x^alpha */ + html += `
+
ИНТЕРАКТИВ 1
Главный визуализатор $y = x^\\alpha$
+
Меняй $\\alpha$ ползунком — наблюдай, как меняется график. Snap-точки: $-2,\\ -1,\\ -\\tfrac{1}{2},\\ 0,\\ \\tfrac{1}{2},\\ 1,\\ 2,\\ 3$. Точка $(1; 1)$ всегда на графике.
+
+ +
+
+ + + + +
+
+
+
`; + + /* INTERACTIVE 2 — калькулятор D и E */ + html += `
+
ИНТЕРАКТИВ 2
Калькулятор $D$ и $E$
+
Введи показатель $\\alpha$ (целое, дробное вида $p/q$ или иррациональное вроде $\\sqrt{2} \\approx 1{,}41$, $\\pi \\approx 3{,}14$). Получи области определения и значений.
+
+ $\\alpha$ = + + +
+
+ +
`; + + /* INTERACTIVE 3 — квикфайр «Какая функция?» */ + html += `
+
ИНТЕРАКТИВ 3
Какая функция? Угадай $\\alpha$
+
По описанию графика выбери правильное значение $\\alpha$ из 4 вариантов. 8 заданий.
+
Задача 1 / 8Очки: 0 / 8
+
+
+ +
+
`; + + /* INTERACTIVE 4 — тренажёр свойств */ + html += `
+
ИНТЕРАКТИВ 4
Тренажёр: свойства степенной функции
+
6 задач: подсчёт корней, вычисление значения, поиск $\\alpha$. Допуск $\\pm 0{,}05$.
+
Задача 1 / 6Очки: 0 / 6
+
+
+ ответ = + + + +
+ +
`; + + html += secNav('p1', 'p3'); html += readButton('p2'); + box.innerHTML = html; renderMath(box); + + /* IV1 — главный визуализатор y = x^alpha */ + (function(){ + const sa = document.getElementById('p2-iv1-sa'); + const aL = document.getElementById('p2-iv1-a'); + const svg = document.getElementById('p2-iv1-svg'); + const desc = document.getElementById('p2-iv1-desc'); + const cb1 = document.getElementById('p2-iv1-cb-1'); + const cb2 = document.getElementById('p2-iv1-cb-2'); + const cb3 = document.getElementById('p2-iv1-cb-3'); + const cbh = document.getElementById('p2-iv1-cb-h'); + const SNAP = [-2, -1, -0.5, 0, 0.5, 1, 2, 3]; + const seen = new Set(); + let _done = false; + + function alphaColor(a){ + if(a < 0) return '#ef4444'; + if(!Number.isInteger(a)) return '#f59e0b'; + if(a % 2 === 0) return '#a78bfa'; + return '#3b82f6'; + } + function alphaLabel(a){ + if(Math.abs(a) < 0.001) return '0'; + if(Math.abs(a - Math.round(a)) < 0.001) return String(Math.round(a)); + if(Math.abs(a - 0.5) < 0.001) return '\\tfrac{1}{2}'; + if(Math.abs(a + 0.5) < 0.001) return '-\\tfrac{1}{2}'; + if(Math.abs(a - 1/3) < 0.01) return '\\tfrac{1}{3}'; + return (+a.toFixed(2)).toString(); + } + function powSafe(x, a){ + if(x === 0){ + if(a > 0) return 0; + return NaN; + } + if(x > 0) return Math.pow(x, a); + // x < 0 + if(Number.isInteger(a)) return Math.pow(x, a); + // дробное alpha с нечётным знаменателем → пробуем как корень + // ищем простую рациональную аппроксимацию для snap-значений + const snapMatch = SNAP.find(s => Math.abs(s - a) < 0.001); + if(snapMatch !== undefined){ + // 1/3 - кубический корень определён для x<0 + if(Math.abs(snapMatch - 1/3) < 0.001) return -Math.pow(-x, 1/3); + } + return NaN; + } + function describe(a){ + const eps = 0.05; + if(Math.abs(a) < eps) return '$\\alpha = 0$: $y = x^0 = 1$ при $x \\ne 0$. Горизонтальная прямая (с выколотой точкой в $x = 0$).'; + if(Math.abs(a - Math.round(a)) < eps){ + const n = Math.round(a); + if(n > 0){ + if(n % 2 === 0){ + return 'Натуральное чётное ($\\alpha = '+n+'$): парабола $'+n+'$-й степени.
$D = \\mathbb{R}$, $E = [0; +\\infty)$. Чётная функция (симметрия отн. $Oy$).
Убывает на $(-\\infty; 0]$, возрастает на $[0; +\\infty)$.'; + } else { + return 'Натуральное нечётное ($\\alpha = '+n+'$): $y = x^{'+n+'}$.
$D = \\mathbb{R}$, $E = \\mathbb{R}$. Нечётная функция (симметрия отн. начала координат).
Возрастает на всей $\\mathbb{R}$.'; + } + } else { + if(n % 2 === 0){ + return 'Целое отрицательное чётное ($\\alpha = '+n+'$): $y = \\dfrac{1}{x^{'+(-n)+'}}$.
$D = \\mathbb{R} \\setminus \\{0\\}$, $E = (0; +\\infty)$. Чётная. Асимптоты: $x = 0$, $y = 0$.'; + } else { + return 'Целое отрицательное нечётное ($\\alpha = '+n+'$): гипербола $y = \\dfrac{1}{x^{'+(-n)+'}}$.
$D = \\mathbb{R} \\setminus \\{0\\}$, $E = \\mathbb{R} \\setminus \\{0\\}$. Нечётная. Асимптоты: $x = 0$, $y = 0$.'; + } + } + } + if(Math.abs(a - 0.5) < eps){ + return '$\\alpha = \\tfrac{1}{2}$: квадратный корень $y = \\sqrt{x}$.
$D = [0; +\\infty)$, $E = [0; +\\infty)$. Возрастает на области определения.'; + } + if(Math.abs(a + 0.5) < eps){ + return '$\\alpha = -\\tfrac{1}{2}$: $y = \\dfrac{1}{\\sqrt{x}}$.
$D = (0; +\\infty)$, $E = (0; +\\infty)$. Убывает. Асимптоты $x = 0$, $y = 0$.'; + } + if(a > 0){ + return 'Дробный положительный ($\\alpha \\approx '+(+a.toFixed(2))+'$): $D = [0; +\\infty)$, возрастает.'; + } + return 'Отрицательный ($\\alpha \\approx '+(+a.toFixed(2))+'$): $D = (0; +\\infty)$, убывает. Асимптоты $x = 0$, $y = 0$.'; + } + function draw(){ + let a = +sa.value; + a = snapToValue(a, SNAP, 0.08); + aL.textContent = alphaLabel(a); + const xmin = -3.5, xmax = 3.5, ymin = -3.5, ymax = 3.5; + const W = 480, H = 360, pad = 24; + const ax = axes2D(W, H, pad, xmin, xmax, ymin, ymax); + let g = ax.content; + // эталоны + if(cb1.checked) g += plotFunc(x => x, xmin, xmax, ax.toX, ax.toY, '#94a3b8'); + if(cb2.checked) g += plotFunc(x => x*x, xmin, xmax, ax.toX, ax.toY, '#a78bfa'); + if(cb3.checked) g += plotFunc(x => x*x*x, xmin, xmax, ax.toX, ax.toY, '#3b82f6'); + if(cbh.checked){ + g += plotFunc(x => 1/x, 0.001, xmax, ax.toX, ax.toY, '#ef4444'); + g += plotFunc(x => 1/x, xmin, -0.001, ax.toX, ax.toY, '#ef4444'); + } + // главный график + const col = alphaColor(a); + const isIntA = Math.abs(a - Math.round(a)) < 0.001; + const intA = Math.round(a); + if(isIntA && intA !== 0){ + // целое: рисуем на обеих сторонах + if(intA > 0){ + g += ''; + g += plotFunc(x => powSafe(x, intA), xmin, xmax, ax.toX, ax.toY, col, 300); + } else { + // отрицательное целое + g += plotFunc(x => powSafe(x, intA), 0.05, xmax, ax.toX, ax.toY, col, 200); + g += plotFunc(x => powSafe(x, intA), xmin, -0.05, ax.toX, ax.toY, col, 200); + g += asymptote('v', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#cbd5e1'); + } + } else if(Math.abs(a) < 0.001){ + // y = 1 + g += plotFunc(x => 1, 0.001, xmax, ax.toX, ax.toY, col); + g += plotFunc(x => 1, xmin, -0.001, ax.toX, ax.toY, col); + } else { + // дробное / иррациональное + if(a > 0){ + g += plotFunc(x => Math.pow(x, a), 0.001, xmax, ax.toX, ax.toY, col, 250); + // если a = 1/3 — рисуем и слева + if(Math.abs(a - 1/3) < 0.05){ + g += plotFunc(x => -Math.pow(-x, a), xmin, -0.001, ax.toX, ax.toY, col, 250); + } + } else { + g += plotFunc(x => Math.pow(x, a), 0.05, xmax, ax.toX, ax.toY, col, 250); + g += asymptote('v', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#cbd5e1'); + } + } + // точка (1, 1) + g += pointWithDrop(1, 1, ax.toX, ax.toY, '#0f172a', '(1; 1)'); + svg.innerHTML = g; + desc.innerHTML = describe(a); + renderMath(desc); + const key = (+a.toFixed(2)).toString(); + seen.add(key); + if(!_done && seen.size >= 6){ _done = true; addXp(10, 'p2-iv1'); bumpProgress('p2', 15); } + } + sa.addEventListener('input', draw); + [cb1,cb2,cb3,cbh].forEach(c => c.addEventListener('change', draw)); + draw(); + })(); + + /* IV2 — калькулятор D и E */ + (function(){ + const aI = document.getElementById('p2-iv2-a'); + const out = document.getElementById('p2-iv2-out'); + const fb = document.getElementById('p2-iv2-fb'); + const go = document.getElementById('p2-iv2-go'); + const used = new Set(); + let _done = false; + + function findFraction(a, maxDen){ + maxDen = maxDen || 20; + for(let n = 2; n <= maxDen; n++){ + for(let m = -3*n; m <= 3*n; m++){ + if(m === 0) continue; + if(Math.abs(m/n - a) < 0.005){ + const g = gcd(Math.abs(m), n); + return { num: m/g, den: n/g }; + } + } + } + return null; + } + function analyze(){ + const a = +aI.value; + if(!isFinite(a)){ feedback(fb, false, '✗ Введи число.'); return; } + let D = '', E = '', kind = ''; + const isInt = Math.abs(a - Math.round(a)) < 0.005; + const intA = Math.round(a); + + if(isInt){ + if(intA === 0){ + kind = '$\\alpha = 0$: константа $y = 1$'; + D = '$D = \\mathbb{R} \\setminus \\{0\\}$ (точка $x = 0$ исключена)'; + E = '$E = \\{1\\}$'; + } else if(intA > 0){ + if(intA % 2 === 0){ + kind = 'Натуральное чётное $\\alpha = '+intA+'$ → парабола'; + D = '$D = \\mathbb{R}$ — определена везде'; + E = '$E = [0; +\\infty)$ — значения неотрицательны'; + } else { + kind = 'Натуральное нечётное $\\alpha = '+intA+'$'; + D = '$D = \\mathbb{R}$ — определена везде'; + E = '$E = \\mathbb{R}$ — принимает любые значения'; + } + } else { + if(intA % 2 === 0){ + kind = 'Целое отрицательное чётное $\\alpha = '+intA+'$ → гипербола'; + D = '$D = \\mathbb{R} \\setminus \\{0\\}$'; + E = '$E = (0; +\\infty)$'; + } else { + kind = 'Целое отрицательное нечётное $\\alpha = '+intA+'$'; + D = '$D = \\mathbb{R} \\setminus \\{0\\}$'; + E = '$E = \\mathbb{R} \\setminus \\{0\\}$'; + } + } + } else { + const fr = findFraction(a); + if(fr){ + const { num, den } = fr; + const denOdd = den % 2 === 1; + if(num > 0){ + if(denOdd){ + kind = 'Дробный $\\alpha = \\dfrac{'+num+'}{'+den+'}$ — знаменатель нечётный'; + D = '$D = \\mathbb{R}$ (корень $\\sqrt['+den+']{x^{'+num+'}}$ определён везде)'; + E = (num % 2 === 0) ? '$E = [0; +\\infty)$' : '$E = \\mathbb{R}$'; + } else { + kind = 'Дробный $\\alpha = \\dfrac{'+num+'}{'+den+'}$ — знаменатель чётный'; + D = '$D = [0; +\\infty)$ (корень чётной степени)'; + E = '$E = [0; +\\infty)$'; + } + } else { + kind = 'Дробный отрицательный $\\alpha = \\dfrac{'+num+'}{'+den+'}$'; + D = denOdd ? '$D = \\mathbb{R} \\setminus \\{0\\}$' : '$D = (0; +\\infty)$'; + E = (Math.abs(num) % 2 === 0) ? '$E = (0; +\\infty)$' : (denOdd ? '$E = \\mathbb{R} \\setminus \\{0\\}$' : '$E = (0; +\\infty)$'); + } + } else { + // иррациональное + if(a > 0){ + kind = '$\\alpha \\approx '+(+a.toFixed(3))+'$ — рассматриваем как иррациональное'; + D = '$D = [0; +\\infty)$ (для иррационального $\\alpha > 0$, включая $x = 0$)'; + E = '$E = [0; +\\infty)$'; + } else { + kind = '$\\alpha \\approx '+(+a.toFixed(3))+'$ — иррациональное отрицательное'; + D = '$D = (0; +\\infty)$'; + E = '$E = (0; +\\infty)$'; + } + } + } + out.innerHTML = '
'+kind+'
' + + '
' + D + '
' + + '
' + E + '
'; + renderMath(out); + feedback(fb, true, '✓ Готово.'); + used.add((+a.toFixed(3)).toString()); + if(!_done && used.size >= 4){ _done = true; addXp(10, 'p2-iv2'); bumpProgress('p2', 15); } + } + go.addEventListener('click', analyze); + aI.addEventListener('keydown', e => { if(e.key === 'Enter') analyze(); }); + })(); + + /* IV3 — квикфайр «Какая функция?» */ + (function(){ + const Q = [ + { q:'Парабола $y = x^2$', opts:['1','2','3','-1'], ans:1 }, + { q:'Кубическая функция $y = x^3$', opts:['1','2','3','-1'], ans:2 }, + { q:'Гипербола $y = \\dfrac{1}{x}$', opts:['1','-1','-2','1/2'], ans:1 }, + { q:'Прямая $y = x$', opts:['0','1','2','-1'], ans:1 }, + { q:'Корень $y = \\sqrt{x}$', opts:['2','-2','1/2','1/3'], ans:2 }, + { q:'Гипербола $y = \\dfrac{1}{x^2}$', opts:['-1','-2','2','1/2'], ans:1 }, + { q:'Кубический корень $y = \\sqrt[3]{x}$', opts:['3','-1/3','1/3','1/2'], ans:2 }, + { q:'Парабола 4-й степени $y = x^4$', opts:['2','3','4','-4'], ans:2 }, + ]; + let i = 0, score = 0; + const qEl = document.getElementById('p2-iv3-q'); + const oEl = document.getElementById('p2-iv3-opts'); + const fb = document.getElementById('p2-iv3-fb'); + const iEl = document.getElementById('p2-iv3-i'); + const sEl = document.getElementById('p2-iv3-s'); + + function show(){ + if(i >= Q.length){ + qEl.innerHTML = 'Готово! Результат: ' + score + ' / ' + Q.length; + oEl.innerHTML = ''; + if(score === Q.length){ addXp(15, 'p2-iv3'); bumpProgress('p2', 25); } + else if(score >= 5){ addXp(8, 'p2-iv3'); bumpProgress('p2', 15); } + return; + } + iEl.textContent = (i+1); + sEl.textContent = score; + const item = Q[i]; + qEl.innerHTML = item.q; + oEl.innerHTML = item.opts.map((o, k) => '').join(''); + fb.style.display = 'none'; + renderMath(qEl); renderMath(oEl); + 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, '✗ Неверно. Правильно: $\\alpha = '+item.opts[item.ans]+'$. Дальше ▶'); + sEl.textContent = score; + oEl.querySelectorAll('button').forEach(x => x.disabled = true); + i++; + setTimeout(show, 1200); + }); + }); + } + document.getElementById('p2-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); }); + show(); + })(); + + /* IV4 — тренажёр свойств */ + (function(){ + const Q = [ + { q:'Сколько решений у уравнения $x^4 = 16$?', ans:2, hint:'$x = \\pm 2$ — два корня' }, + { q:'Сколько решений у уравнения $x^3 = -8$?', ans:1, hint:'$x = -2$ — единственный действительный корень' }, + { q:'Найдите $y(4)$ для $y = x^{3/2}$', ans:8, hint:'$4^{3/2} = (\\sqrt{4})^3 = 2^3 = 8$' }, + { q:'Чему равно $\\alpha$ у функции $y = \\sqrt[3]{x^4}$? (десятичная)', ans:4/3, hint:'$\\sqrt[3]{x^4} = x^{4/3} \\approx 1{,}33$' }, + { q:'Для $y = x^{-2}$ найди $y(0{,}5)$', ans:4, hint:'$0{,}5^{-2} = \\dfrac{1}{0{,}25} = 4$' }, + { q:'При $\\alpha = \\dfrac{1}{3}$ найди $y(-27)$', ans:-3, hint:'$\\sqrt[3]{-27} = -3$' }, + ]; + let i = 0, score = 0; + function show(){ + if(i >= Q.length){ + document.getElementById('p2-iv4-q').innerHTML = 'Готово! Результат: ' + score + ' / ' + Q.length; + if(score === Q.length){ addXp(15, 'p2-iv4'); bumpProgress('p2', 25); } + else if(score >= 4){ addXp(8, 'p2-iv4'); bumpProgress('p2', 15); } + return; + } + document.getElementById('p2-iv4-i').textContent = (i+1); + document.getElementById('p2-iv4-s').textContent = score; + document.getElementById('p2-iv4-q').innerHTML = Q[i].q; + document.getElementById('p2-iv4-ans').value = ''; + renderMath(document.getElementById('p2-iv4-q')); + document.getElementById('p2-iv4-fb').style.display = 'none'; + } + function go(){ + if(i >= Q.length) return; + const fb = document.getElementById('p2-iv4-fb'); + const raw = document.getElementById('p2-iv4-ans').value.replace(',', '.'); + const ans = parseFloat(raw); + if(isNaN(ans)){ feedback(fb, false, '✗ Введи число.'); return; } + if(Math.abs(ans - Q[i].ans) < 0.05){ score++; feedback(fb, true, '✓ Верно! '+Q[i].hint+'. Дальше ▶'); } + else feedback(fb, false, '✗ Неверно. Ответ $\\approx '+(+Q[i].ans.toFixed(2))+'$ ('+Q[i].hint+'). Дальше ▶'); + document.getElementById('p2-iv4-s').textContent = score; + i++; + setTimeout(show, 1300); + } + document.getElementById('p2-iv4-go').addEventListener('click', go); + document.getElementById('p2-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); }); + document.getElementById('p2-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); }); + show(); + })(); + wireReadBtn('p2'); }