feat(alg11 ch3 wave2): §8 «Логарифмическая функция» + обратная к показательной

This commit is contained in:
Maxim Dolgolyov
2026-05-29 12:15:24 +03:00
parent aee927a3b1
commit 2a987f01d0
+449 -5
View File
@@ -1059,15 +1059,459 @@ function buildP7(){
function buildP8(){
const box = document.getElementById('p8-body');
let html = '';
html += makeCard('theory', 'В разработке', '8.0', `
<p>Содержание параграфа <b>Логарифмическая функция</b> будет добавлено в Phase 1+.</p>
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 0 — skeleton. Здесь появятся теория, примеры и интерактивы.</p>
<p style="color:var(--muted);font-size:.9rem">Ключевая формула: $y = \log_a x$</p>
`);
/* === ТЕОРИЯ === */
html += makeCard('theory', 'Определение и графики', '8.1', `
<p>Функция вида $y = \\log_a x$, где $a > 0$, $a \\ne 1$, $x > 0$, называется <b>логарифмической</b>.</p>
<p>Логарифмическая функция — <b>обратная</b> к показательной $y = a^x$ (см. §4). Это значит, что:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.75">
<li>$a^{\\log_a x} = x$ при $x > 0$;</li>
<li>$\\log_a a^x = x$ при любом $x \\in \\mathbb{R}$.</li>
</ul>
<p>Графики $y = \\log_a x$ и $y = a^x$ <b>симметричны относительно прямой $y = x$</b> — это общее свойство любой пары взаимно обратных функций.</p>
<p>Примеры логарифмических функций:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.75">
<li>$y = \\log_2 x$, $y = \\log_3 x$, $y = \\log_5 x$ — основания больше единицы;</li>
<li>$y = \\lg x$ — десятичный логарифм ($a = 10$);</li>
<li>$y = \\ln x$ — натуральный логарифм ($a = e \\approx 2{,}718$);</li>
<li>$y = \\log_{1/2} x$, $y = \\log_{0{,}3} x$ — основания между нулём и единицей.</li>
</ul>
<p>График логарифмической функции:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.75">
<li>при $a > 1$ — <b>возрастающая</b> кривая, медленно стремящаяся к $+\\infty$ при $x \\to +\\infty$;</li>
<li>при $0 < a < 1$ — <b>убывающая</b> кривая, стремящаяся к $-\\infty$ при $x \\to +\\infty$.</li>
</ul>
<p>Все графики $y = \\log_a x$ проходят через точку $(1; 0)$ — ведь $\\log_a 1 = 0$ при любом $a > 0$, $a \\ne 1$. Также через $(a; 1)$, потому что $\\log_a a = 1$.</p>`);
html += makeCard('rule', 'Свойства логарифмической функции', '8.2', `
<p>Сведём все ключевые свойства функции $y = \\log_a x$ в одну таблицу — отдельно для двух случаев $a > 1$ и $0 < a < 1$:</p>
<div style="overflow-x:auto;margin:10px 0">
<table style="width:100%;border-collapse:collapse;font-size:.88rem">
<thead>
<tr style="background:var(--sec-acc-soft)">
<th style="padding:8px;border:1px solid var(--border);text-align:left">Свойство</th>
<th style="padding:8px;border:1px solid var(--border);text-align:left;color:#1d4ed8">$a > 1$</th>
<th style="padding:8px;border:1px solid var(--border);text-align:left;color:#c2410c">$0 < a < 1$</th>
</tr>
</thead>
<tbody>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>$D(y)$ — область определения</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$(0; +\\infty)$ — только положительные $x$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>$E(y)$ — область значений</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$\\mathbb{R}$ — вся числовая прямая</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Нули функции</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$x = 1$ (так как $\\log_a 1 = 0$)</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>$y > 0$ при</b></td><td style="padding:7px;border:1px solid var(--border)">$x > 1$</td><td style="padding:7px;border:1px solid var(--border)">$0 < x < 1$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>$y < 0$ при</b></td><td style="padding:7px;border:1px solid var(--border)">$0 < x < 1$</td><td style="padding:7px;border:1px solid var(--border)">$x > 1$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Монотонность</b></td><td style="padding:7px;border:1px solid var(--border)"><b>возрастает</b> на $(0; +\\infty)$</td><td style="padding:7px;border:1px solid var(--border)"><b>убывает</b> на $(0; +\\infty)$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Контрольные точки</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$(1; 0)$ и $(a; 1)$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Асимптота</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$x = 0$ (ось ординат)</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>При $x \\to +\\infty$</b></td><td style="padding:7px;border:1px solid var(--border)">$y \\to +\\infty$</td><td style="padding:7px;border:1px solid var(--border)">$y \\to -\\infty$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>При $x \\to 0^+$</b></td><td style="padding:7px;border:1px solid var(--border)">$y \\to -\\infty$</td><td style="padding:7px;border:1px solid var(--border)">$y \\to +\\infty$</td></tr>
</tbody>
</table>
</div>
<p><b>Ключевая идея:</b> два графика $y = \\log_a x$ и $y = \\log_{1/a} x$ симметричны относительно оси $Ox$, потому что $\\log_{1/a} x = -\\log_a x$.</p>`);
html += makeCard('example', 'Обратная функция и применение', '8.3', `
<p><b>Логарифм и показательная — обратные функции.</b> Это означает, что они «отменяют» друг друга:</p>
<p style="text-align:center;font-size:1.05rem">$a^{\\log_a x} = x$ (при $x > 0$),$\\quad \\log_a a^x = x$ (при любом $x$).</p>
<p>Графически: <b>график $y = \\log_a x$ получается из графика $y = a^x$ зеркальным отражением относительно прямой $y = x$</b>. Каждая точка $(p; q)$ на показательной кривой переходит в точку $(q; p)$ на логарифмической.</p>
<p>Например, для $a = 2$: точка $(3; 8)$ лежит на $y = 2^x$, а симметричная ей $(8; 3)$ — на $y = \\log_2 x$.</p>
<p style="margin-top:10px"><b>Применение логарифмической функции в реальной жизни</b> — там, где величина изменяется в очень широких пределах и удобно «сжать» шкалу:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.85">
<li><b>Громкость звука (децибелы):</b> $L = 20 \\lg \\dfrac{p}{p_0}$ — рост звукового давления в 10 раз даёт $+20$ дБ.</li>
<li><b>Магнитуда землетрясения (шкала Рихтера):</b> $M = \\lg E - \\lg E_0$ — увеличение магнитуды на 1 соответствует росту энергии в 10 раз.</li>
<li><b>Кислотность среды (pH):</b> $\\mathrm{pH} = -\\lg [H^+]$ — pH крови $\\approx 7{,}4$, желудочного сока $\\approx 1{,}5$.</li>
<li><b>Объём информации:</b> $I = \\log_2 N$ бит — чтобы закодировать одну букву из $32$ символов, нужно $\\log_2 32 = 5$ бит.</li>
<li><b>Яркость звёзд (звёздная величина):</b> $m_1 - m_2 = -2{,}5 \\lg(F_1/F_2)$ — астрономы со времён Гиппарха используют логарифмическую шкалу.</li>
<li><b>Психофизика (закон Вебера — Фехнера):</b> субъективная сила ощущения пропорциональна логарифму раздражителя.</li>
</ul>
<p>Логарифмическая шкала идеально подходит для природы: наше ухо, глаз и нервная система воспринимают раздражители <b>логарифмически</b>.</p>`);
/* === ИНТЕРАКТИВЫ === */
/* IV1 — двухпанельный визуализатор y = log_a x */
html += `<div class="wg" id="p8-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Двухпанельный визуализатор $y = \\log_a x$</div></div>
<div class="wg-help">Меняй основание $a$ ползунком — наблюдай, как меняется характер логарифмической функции. <b>Левая панель</b> для $a > 1$ (возрастающая, синий), <b>правая</b> — для $0 < a < 1$ (убывающая, оранжевый). Snap-точки: $a \\in \\{0{,}1;\\ 0{,}25;\\ 0{,}5;\\ 1;\\ 2;\\ e;\\ 3;\\ 10\\}$. Точка $(1; 0)$ — всегда на графике, ось $Oy$ — вертикальная асимптота.</div>
<div class="sliders">
<label>Основание $a$ = <b id="p8-iv1-a">2</b><input type="range" id="p8-iv1-sa" min="0.1" max="10" step="0.05" value="2"></label>
</div>
<div style="background:var(--card);border-radius:9px;padding:10px;text-align:center"><svg id="p8-iv1-svg" viewBox="0 0 720 360" width="100%" style="max-width:720px;height:auto"></svg></div>
<div id="p8-iv1-desc" style="margin-top:10px;padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;line-height:1.6;min-height:80px"></div>
</div>`;
/* IV2 — сравнение y = a^x и y = log_a x (симметрия) */
html += `<div class="wg" id="p8-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Сравнение $y = a^x$ и $y = \\log_a x$</div></div>
<div class="wg-help">Ползунок меняет основание $a > 1$. На одних осях рисуются обе функции — показательная (синий) и логарифмическая (оранжевый), а серым пунктиром — ось симметрии $y = x$. Нажми «Показать симметрию», чтобы увидеть пары симметричных точек.</div>
<div class="sliders">
<label>Основание $a$ = <b id="p8-iv2-a">2</b><input type="range" id="p8-iv2-sa" min="1.5" max="5" step="0.1" value="2"></label>
</div>
<div style="background:var(--card);border-radius:9px;padding:10px;text-align:center"><svg id="p8-iv2-svg" viewBox="0 0 480 420" width="100%" style="max-width:480px;height:auto"></svg></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p8-iv2-sym">Показать симметрию</button>
<button class="btn" id="p8-iv2-hide">Скрыть</button>
</div>
<div id="p8-iv2-desc" style="margin-top:10px;padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;line-height:1.6;min-height:60px"></div>
</div>`;
/* IV3 — квикфайр «Возрастает или убывает?» */
html += `<div class="wg" id="p8-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Возрастает или убывает?</div></div>
<div class="wg-help">Для каждой логарифмической функции определи характер монотонности. 8 заданий.</div>
<div class="score-display"><span>Задача <b id="p8-iv3-i">1</b> / 8</span><span>Очки: <b id="p8-iv3-s">0</b> / 8</span></div>
<div id="p8-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.1rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p8-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;max-width:420px;margin:0 auto"></div>
<div class="feedback" id="p8-iv3-fb"></div>
<div class="actions" style="justify-content:center"><button class="btn" id="p8-iv3-restart">Начать заново</button></div>
</div>`;
/* IV4 — тренажёр значений логарифмической функции */
html += `<div class="wg" id="p8-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр свойств</div></div>
<div class="wg-help">Вычисли значение логарифмической функции. Введи число (целое или десятичное до 2 знаков, допуск $\\pm 0{,}05$). 6 задач.</div>
<div class="score-display"><span>Задача <b id="p8-iv4-i">1</b> / 6</span><span>Очки: <b id="p8-iv4-s">0</b> / 6</span></div>
<div id="p8-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.15rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p8-iv4-ans" class="tinp" style="width:120px;text-align:center" step="0.01">
<button class="btn primary" id="p8-iv4-go">Проверить</button>
<button class="btn" id="p8-iv4-start">Заново</button>
</div>
<div class="feedback" id="p8-iv4-fb"></div>
</div>`;
html += secNavFor('p8');
html += readButton('p8');
box.innerHTML = html;
renderMath(box);
/* === IV1 — двухпанельный визуализатор y = log_a x === */
(function(){
const sa = document.getElementById('p8-iv1-sa');
const aL = document.getElementById('p8-iv1-a');
const svg = document.getElementById('p8-iv1-svg');
const desc = document.getElementById('p8-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_POINT10 = '#10b981';
const COL_POINTA1 = '#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 = -0.5, xmax = 8, ymin = -4, ymax = 4;
const ax = axes2D(W, H, pad, xmin, xmax, ymin, ymax);
let g = '<g transform="translate('+ox+',0)">';
g += ax.content;
const col = isGrow ? COL_GROW : COL_DECAY;
// заголовок панели
g += '<text x="'+(W/2)+'" y="16" font-family="Inter,sans-serif" font-size="13" font-weight="700" text-anchor="middle" fill="'+(active?col:COL_NEUTRAL)+'">'
+ (isGrow ? 'a &gt; 1 — возрастающая' : '0 &lt; a &lt; 1 — убывающая')
+ '</text>';
if(!active){
g += '<rect x="'+pad+'" y="'+(pad+8)+'" width="'+(W-2*pad)+'" height="'+(H-2*pad-8)+'" fill="#f1f5f9" opacity=".55"/>';
g += '<text x="'+(W/2)+'" y="'+(H/2)+'" font-family="Inter,sans-serif" font-size="12" font-weight="600" text-anchor="middle" fill="#94a3b8">';
g += isGrow ? 'выбери a &gt; 1' : 'выбери 0 &lt; a &lt; 1';
g += '</text>';
g += '</g>';
return g;
}
// вертикальная асимптота x = 0
g += asymptote('v', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#94a3b8');
// график y = log_a x — только при x > 0
const lnA = Math.log(a);
g += plotFunc(x => (x > 0 ? Math.log(x) / lnA : NaN), 0.001, xmax, ax.toX, ax.toY, col, 300);
// точка (1; 0) — зелёная
g += pointWithDrop(1, 0, ax.toX, ax.toY, COL_POINT10, '(1; 0)');
// точка (a; 1) — красная (если a в пределах xmax)
if(a > 0 && a <= xmax){
g += pointWithDrop(a, 1, ax.toX, ax.toY, COL_POINTA1, '('+aPlain(a)+'; 1)');
}
g += '</g>';
return g;
}
function drawConstPanel(){
// обе панели — серые заглушки для a = 1
const W = 720, H = 360, pad = 28;
const xmin = -0.5, xmax = 8, ymin = -4, ymax = 4;
let out = '';
// левая
{
const ax = axes2D(360, H, pad, xmin, xmax, ymin, ymax);
let g = '<g transform="translate(0,0)">' + ax.content;
g += '<text x="180" y="16" font-family="Inter,sans-serif" font-size="13" font-weight="700" text-anchor="middle" fill="'+COL_NEUTRAL+'">a = 1: $\\log_1 x$ не определён</text>';
g += asymptote('v', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#cbd5e1');
g += '</g>';
out += g;
}
// правая
{
const ax = axes2D(360, H, pad, xmin, xmax, ymin, ymax);
let g = '<g transform="translate(360,0)">' + ax.content;
g += '<text x="180" y="16" font-family="Inter,sans-serif" font-size="13" font-weight="700" text-anchor="middle" fill="'+COL_NEUTRAL+'">a = 1 — не логарифмическая</text>';
g += asymptote('v', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#cbd5e1');
g += '</g>';
out += g;
}
return out;
}
function describe(a){
if(Math.abs(a - 1) < 0.01){
return '<b>$a = 1$:</b> функция $y = \\log_1 x$ <b>не определена</b> — основание логарифма не должно равняться $1$.';
}
if(a > 1){
const aTxt = aLabel(a);
return '<b>$a = '+aTxt+' > 1$ — функция возрастает.</b><br>'
+ '$D = (0; +\\infty)$, $E = \\mathbb{R}$. Проходит через $(1; 0)$ и $('+aPlain(a)+'; 1)$.<br>'
+ 'При $x \\to +\\infty$: $y \\to +\\infty$. При $x \\to 0^+$: $y \\to -\\infty$ (асимптота $x = 0$).<br>'
+ '$y > 0$ при $x > 1$; $y < 0$ при $0 < x < 1$.';
}
// 0 < a < 1
const aTxt = aLabel(a);
return '<b>$a = '+aTxt+'$, $0 < a < 1$ — функция убывает.</b><br>'
+ '$D = (0; +\\infty)$, $E = \\mathbb{R}$. Проходит через $(1; 0)$ и $('+aPlain(a)+'; 1)$.<br>'
+ 'При $x \\to +\\infty$: $y \\to -\\infty$. При $x \\to 0^+$: $y \\to +\\infty$ (асимптота $x = 0$).<br>'
+ '$y > 0$ при $0 < x < 1$; $y < 0$ при $x > 1$.';
}
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();
} 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 += '<line x1="360" y1="6" x2="360" y2="354" stroke="#e2e8f0" stroke-width="1.5" stroke-dasharray="4 4"/>';
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, 'p8-iv1'); bumpProgress('p8', 15); }
}
sa.addEventListener('input', draw);
draw();
})();
/* === IV2 — сравнение y = a^x и y = log_a x === */
(function(){
const sa = document.getElementById('p8-iv2-sa');
const aL = document.getElementById('p8-iv2-a');
const svg = document.getElementById('p8-iv2-svg');
const desc = document.getElementById('p8-iv2-desc');
const symBtn = document.getElementById('p8-iv2-sym');
const hideBtn = document.getElementById('p8-iv2-hide');
const seen = new Set();
let _done = false;
let showSym = false;
const COL_EXP = '#2563eb';
const COL_LOG = '#ea580c';
const COL_AXIS = '#94a3b8';
function aPlain(a){ return (+a.toFixed(2)).toString(); }
function draw(){
const a = +sa.value;
aL.textContent = aPlain(a);
const W = 480, H = 420, pad = 32;
const xmin = -3, xmax = 6, ymin = -3, ymax = 6;
const ax = axes2D(W, H, pad, xmin, xmax, ymin, ymax);
let g = ax.content;
// ось симметрии y = x — серый пунктир
g += '<line x1="'+ax.toX(xmin)+'" y1="'+ax.toY(xmin)+'" x2="'+ax.toX(ymax)+'" y2="'+ax.toY(ymax)+'" stroke="'+COL_AXIS+'" stroke-width="1.3" stroke-dasharray="6 4"/>';
g += '<text x="'+ax.toX(5.4)+'" y="'+ax.toY(5.8)+'" font-family="Inter,sans-serif" font-size="11" font-weight="600" fill="'+COL_AXIS+'">y = x</text>';
// показательная y = a^x
const lnA = Math.log(a);
g += plotFunc(x => Math.pow(a, x), xmin, xmax, ax.toX, ax.toY, COL_EXP, 240);
// логарифмическая y = log_a x
g += plotFunc(x => (x > 0 ? Math.log(x) / lnA : NaN), 0.001, xmax, ax.toX, ax.toY, COL_LOG, 240);
// подписи кривых
g += '<text x="'+ax.toX(2)+'" y="'+ax.toY(Math.min(Math.pow(a,2)+0.4, 5.6))+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+COL_EXP+'">y = a^x</text>';
const xLogLabel = Math.min(5, Math.pow(a, 1.4));
g += '<text x="'+ax.toX(Math.max(xLogLabel, 2))+'" y="'+ax.toY(Math.log(Math.max(xLogLabel, 2))/lnA - 0.4)+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+COL_LOG+'">y = log_a x</text>';
// ключевые точки
g += pointWithDrop(0, 1, ax.toX, ax.toY, COL_EXP, ''); // (0;1) на y=a^x
g += pointWithDrop(1, 0, ax.toX, ax.toY, COL_LOG, ''); // (1;0) на y=log_a x
// (1; a) на показательной и (a; 1) на логарифмической
if(a <= ymax) g += pointWithDrop(1, a, ax.toX, ax.toY, COL_EXP, '');
if(a <= xmax) g += pointWithDrop(a, 1, ax.toX, ax.toY, COL_LOG, '');
if(showSym){
// линии симметрии: (0;1) ↔ (1;0) и (1;a) ↔ (a;1)
g += '<line x1="'+ax.toX(0)+'" y1="'+ax.toY(1)+'" x2="'+ax.toX(1)+'" y2="'+ax.toY(0)+'" stroke="#16a34a" stroke-width="1.4" stroke-dasharray="4 4" opacity=".85"/>';
if(a <= ymax && a <= xmax){
g += '<line x1="'+ax.toX(1)+'" y1="'+ax.toY(a)+'" x2="'+ax.toX(a)+'" y2="'+ax.toY(1)+'" stroke="#16a34a" stroke-width="1.4" stroke-dasharray="4 4" opacity=".85"/>';
}
// дополнительная пара (2; a^2) ↔ (a^2; 2), если в пределах
const a2 = Math.pow(a, 2);
if(a2 <= ymax && a2 <= xmax){
g += pointWithDrop(2, a2, ax.toX, ax.toY, COL_EXP, '');
g += pointWithDrop(a2, 2, ax.toX, ax.toY, COL_LOG, '');
g += '<line x1="'+ax.toX(2)+'" y1="'+ax.toY(a2)+'" x2="'+ax.toX(a2)+'" y2="'+ax.toY(2)+'" stroke="#16a34a" stroke-width="1.4" stroke-dasharray="4 4" opacity=".85"/>';
}
}
svg.innerHTML = g;
desc.innerHTML = '<b>$y = '+aPlain(a)+'^x$ (синий) и $y = \\log_{'+aPlain(a)+'} x$ (оранжевый)</b> симметричны относительно прямой $y = x$ — это <b>обратные функции</b>.<br>'
+ 'Точка $(0; 1)$ на показательной соответствует точке $(1; 0)$ на логарифмической; точка $(1; '+aPlain(a)+')$ — точке $('+aPlain(a)+'; 1)$.';
renderMath(desc);
const key = (+a.toFixed(1)).toString();
seen.add(key);
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p8-iv2'); bumpProgress('p8', 15); }
}
sa.addEventListener('input', draw);
symBtn.addEventListener('click', () => { showSym = true; draw(); });
hideBtn.addEventListener('click', () => { showSym = false; draw(); });
draw();
})();
/* === IV3 — квикфайр «Возрастает или убывает?» === */
(function(){
const Q = [
{ q:'$y = \\log_2 x$', ans:0 },
{ q:'$y = \\log_{1/2} x$', ans:1 },
{ q:'$y = \\lg x$', ans:0 },
{ q:'$y = \\log_{0{,}1} x$', ans:1 },
{ q:'$y = \\ln x$', ans:0 },
{ q:'$y = \\log_{1/3} x$', ans:1 },
{ q:'$y = \\log_5 x$', ans:0 },
{ q:'$y = \\log_{0{,}5} x$', ans:1 },
];
const OPTS = ['Возрастает', 'Убывает'];
let i = 0, score = 0;
const qEl = document.getElementById('p8-iv3-q');
const oEl = document.getElementById('p8-iv3-opts');
const fb = document.getElementById('p8-iv3-fb');
const iEl = document.getElementById('p8-iv3-i');
const sEl = document.getElementById('p8-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p8-iv3'); bumpProgress('p8', 25); }
else if(score >= 5){ addXp(8, 'p8-iv3'); bumpProgress('p8', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = 'Какая монотонность у функции ' + item.q + ' ?';
oEl.innerHTML = OPTS.map((o, k) => '<button class="btn primary" data-k="'+k+'">'+o+'</button>').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, '&#10003; Верно! Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Правильно: <b>'+OPTS[item.ans]+'</b>. Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1100);
});
});
}
document.getElementById('p8-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* === IV4 — тренажёр свойств === */
(function(){
const Q = [
{ q:'$\\log_3 9 = \\,?$', ans:2, hint:'$3^2 = 9$, поэтому $\\log_3 9 = 2$.' },
{ q:'$\\log_{1/2} 4 = \\,?$', ans:-2, hint:'$(1/2)^{-2} = 4$, поэтому $\\log_{1/2} 4 = -2$.' },
{ q:'$\\lg 100 = \\,?$', ans:2, hint:'$10^2 = 100$, поэтому $\\lg 100 = 2$.' },
{ q:'$\\log_5 1 = \\,?$', ans:0, hint:'$\\log_a 1 = 0$ при любом $a$.' },
{ q:'$\\log_2 \\sqrt{8} = \\,?$', ans:1.5, hint:'$\\sqrt{8} = 2^{3/2}$, поэтому $\\log_2 \\sqrt{8} = \\dfrac{3}{2} = 1{,}5$.' },
{ q:'$\\log_{0{,}1} 100 = \\,?$', ans:-2, hint:'$0{,}1 = 10^{-1}$ и $100 = 10^2$, значит $\\log_{0{,}1} 100 = -2$.' },
];
let i = 0, score = 0;
function show(){
const qEl = document.getElementById('p8-iv4-q');
const iEl = document.getElementById('p8-iv4-i');
const sEl = document.getElementById('p8-iv4-s');
const fb = document.getElementById('p8-iv4-fb');
const ansI = document.getElementById('p8-iv4-ans');
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
if(score === Q.length){ addXp(15, 'p8-iv4'); bumpProgress('p8', 25); }
else if(score >= 4){ addXp(8, 'p8-iv4'); bumpProgress('p8', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
qEl.innerHTML = Q[i].q;
ansI.value = '';
renderMath(qEl);
fb.style.display = 'none';
setTimeout(() => ansI.focus(), 30);
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p8-iv4-fb');
const raw = document.getElementById('p8-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) < 0.05){
score++;
feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶');
} else {
feedback(fb, false, '&#10007; Неверно. Ответ $= '+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
}
document.getElementById('p8-iv4-s').textContent = score;
i++;
setTimeout(show, 1500);
}
document.getElementById('p8-iv4-go').addEventListener('click', go);
document.getElementById('p8-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p8-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p8');
}