feat(alg9 ch4 wave3): §18 «Сумма геом.» + §19 «Бесконечно убывающая»

This commit is contained in:
Maxim Dolgolyov
2026-05-29 09:01:45 +03:00
parent 00bd7cada7
commit b66c688340
+576 -26
View File
@@ -1718,38 +1718,588 @@ function buildP17(){
}
function buildP18(){
const root = document.getElementById('p18-body');
root.innerHTML = `
<div class="card">
<div class="card-header">
<span class="card-icon theory">${ICONS.theory}</span>
<span class="card-title">В разработке</span>
<span class="card-num">§ 18</span>
const box = document.getElementById('p18-body');
let html = '';
html += makeCard('theory', 'Формула суммы $n$ членов', '18.1', `
<p><b>Сумма первых $n$ членов</b> геометрической прогрессии:</p>
<p>$$S_n = \\dfrac{b_1\\,(q^n - 1)}{q - 1},\\quad q \\ne 1.$$</p>
<p>Если $q = 1$ — все члены равны $b_1$, тогда $S_n = n \\cdot b_1$.</p>
<p>Также удобная форма (через $b_n$):</p>
<p>$$S_n = \\dfrac{b_n \\cdot q - b_1}{q - 1},\\quad q \\ne 1.$$</p>`);
html += makeCard('rule', 'Вывод формулы', '18.2', `
<p>Запишем сумму:</p>
<p>$$S_n = b_1 + b_1 q + b_1 q^2 + \\ldots + b_1 q^{n - 1}. \\quad (1)$$</p>
<p>Умножим обе части на $q$:</p>
<p>$$q\\,S_n = b_1 q + b_1 q^2 + b_1 q^3 + \\ldots + b_1 q^n. \\quad (2)$$</p>
<p>Вычтем $(1)$ из $(2)$ — почти все слагаемые сократятся:</p>
<p>$$q\\,S_n - S_n = b_1 q^n - b_1\\ \\Rightarrow\\ S_n(q - 1) = b_1(q^n - 1).$$</p>
<p>При $q \\ne 1$ делим на $q - 1$ и получаем формулу.</p>`);
html += makeCard('example', 'Примеры применения', '18.3', `
<p><b>а)</b> Сумма первых 5 членов прогрессии $2, 4, 8, 16, 32$ ($b_1 = 2,\\ q = 2$):</p>
<p>$$S_5 = \\dfrac{2\\,(2^5 - 1)}{2 - 1} = 2 \\cdot 31 = \\mathbf{62}.$$</p>
<p><b>б)</b> Сумма $1 + 3 + 9 + 27 + \\ldots + 3^{10}$ — это $b_1 = 1,\\ q = 3,\\ n = 11$:</p>
<p>$$S_{11} = \\dfrac{1 \\cdot (3^{11} - 1)}{3 - 1} = \\dfrac{177146}{2} = \\mathbf{88\\,573}.$$</p>
<p><b>в)</b> Особый случай $q = -1$ для $b_1 = 1$: ряд $1, -1, 1, -1, \\ldots$ даёт $S_n = 0$ при чётном $n$ и $S_n = 1$ при нечётном.</p>`);
/* INTERACTIVE 1 — конструктор суммы */
html += `<div class="wg" id="p18-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор суммы $S_n$</div></div>
<div class="wg-help">Двигай $b_1$, $q$, $n$ — увидишь первые члены, подстановку в формулу и итог. Особый случай $q = 1$ обработан.</div>
<div class="sliders">
<label>$b_1$ =<b id="p18-iv1-bv">2</b><input type="range" id="p18-iv1-b1" min="-5" max="10" step="1" value="2"></label>
<label>$q$ =<b id="p18-iv1-qv">2</b><input type="range" id="p18-iv1-q" min="-2" max="2" step="0.5" value="2"></label>
<label>$n$ =<b id="p18-iv1-nv">5</b><input type="range" id="p18-iv1-n" min="2" max="10" step="1" value="5"></label>
</div>
<div id="p18-iv1-terms" style="text-align:center;font-size:1.0rem;padding:10px;background:var(--card);border-radius:9px;margin-bottom:8px"></div>
<div id="p18-iv1-formula" style="text-align:center;font-size:1.05rem;padding:10px;background:var(--card);border-radius:9px;margin-bottom:8px"></div>
<div id="p18-iv1-out" style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:1.0rem;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — калькулятор S_n */
html += `<div class="wg" id="p18-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор $S_n$</div></div>
<div class="wg-help">Введи $b_1$, $q$, $n$ — получишь $q^n$ и полную подстановку в формулу.</div>
<div style="background:var(--card);border-radius:11px;padding:14px;max-width:380px;margin:0 auto 10px">
<div style="display:grid;grid-template-columns:auto 1fr;gap:8px 10px;align-items:center;margin-bottom:10px">
<span>$b_1$ =</span><input type="number" id="p18-iv2-b1" class="tinp" value="2" step="1">
<span>$q$ =</span><input type="number" id="p18-iv2-q" class="tinp" value="3" step="0.5">
<span>$n$ =</span><input type="number" id="p18-iv2-n" class="tinp" value="5" step="1" min="1">
</div>
<div class="card-body">
<p>Содержание параграфа <b>«Сумма геом. прогрессии»</b> будет добавлено в следующих обновлениях.</p>
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 1.</p>
</div>
</div>` + secNav('p17', 'p19') + readButton('p18');
renderMath(root);
<button class="btn primary" id="p18-iv2-go" style="width:100%">Вычислить $S_n$</button>
</div>
<div id="p18-iv2-out" style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:8px;font-size:.95rem;min-height:40px;text-align:center"></div>
</div>`;
/* INTERACTIVE 3 — сравни формулы */
html += `<div class="wg" id="p18-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Сравни формулы: верно или ошибка?</div></div>
<div class="wg-help">Перед тобой утверждение. Реши, верное оно или содержит ошибку в применении формулы $S_n = \\dfrac{b_1(q^n - 1)}{q - 1}$.</div>
<div class="score-display">Задача: <b id="p18-iv3-idx">1</b> / 6 &middot; Очки: <b id="p18-iv3-sc">0</b></div>
<div id="p18-iv3-q" style="text-align:center;font-size:1.0rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p18-iv3-y">Верно</button>
<button class="btn" id="p18-iv3-n">Ошибка</button>
</div>
<div class="feedback" id="p18-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — тренажёр */
html += `<div class="wg" id="p18-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр на $S_n$</div></div>
<div class="wg-help">Вычисляй суммы по формуле $S_n = \\dfrac{b_1(q^n - 1)}{q - 1}$. Ответ — целое число.</div>
<div class="score-display">Задача: <b id="p18-iv4-idx">1</b> / 6 &middot; Очки: <b id="p18-iv4-sc">0</b></div>
<div id="p18-iv4-q" style="text-align:center;font-size:1.0rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center;align-items:center">
<span style="font-family:'JetBrains Mono',monospace">Ответ:</span>
<input type="number" id="p18-iv4-ans" class="tinp" style="width:120px;text-align:center" step="1">
<button class="btn primary" id="p18-iv4-go">Проверить</button>
</div>
<div class="feedback" id="p18-iv4-fb"></div>
</div>`;
box.innerHTML = html + secNav('p17', 'p19') + readButton('p18');
renderMath(box);
/* ===== IV1 wiring ===== */
(function(){
const b1Sl = document.getElementById('p18-iv1-b1');
const qSl = document.getElementById('p18-iv1-q');
const nSl = document.getElementById('p18-iv1-n');
const bv = document.getElementById('p18-iv1-bv');
const qv = document.getElementById('p18-iv1-qv');
const nv = document.getElementById('p18-iv1-nv');
const termsEl = document.getElementById('p18-iv1-terms');
const fEl = document.getElementById('p18-iv1-formula');
const outEl = document.getElementById('p18-iv1-out');
let bumped = false;
function fmtQ(q){
if (Math.abs(q - 0.5) < 1e-9) return '\\dfrac{1}{2}';
if (Math.abs(q + 0.5) < 1e-9) return '-\\dfrac{1}{2}';
if (Math.abs(q - 1.5) < 1e-9) return '\\dfrac{3}{2}';
if (Math.abs(q + 1.5) < 1e-9) return '-\\dfrac{3}{2}';
return fmt(q);
}
function redraw(){
const b1 = +b1Sl.value, q = +qSl.value, n = +nSl.value;
bv.textContent = b1; qv.textContent = q; nv.textContent = n;
// первые n членов
const terms = [];
for (let i = 0; i < n; i++) terms.push(b1 * Math.pow(q, i));
const sum = terms.reduce((a,b)=>a+b, 0);
const termsStr = terms.map(t => fmt(+t.toFixed(4))).join('\\ +\\ ');
termsEl.innerHTML = '<b>Члены:</b> $' + termsStr + '$';
if (Math.abs(q - 1) < 1e-9){
fEl.innerHTML = '<b>Особый случай $q = 1$:</b> $S_n = n \\cdot b_1 = ' + n + ' \\cdot ' + b1 + ' = \\mathbf{' + (n*b1) + '}$.';
outEl.innerHTML = '$S_{' + n + '} = \\mathbf{' + (n*b1) + '}$';
} else {
const qN = Math.pow(q, n);
fEl.innerHTML = '$S_{' + n + '} = \\dfrac{b_1(q^n - 1)}{q - 1} = \\dfrac{' + b1 + '\\,((' + fmtQ(q) + ')^{' + n + '} - 1)}{' + fmtQ(q) + ' - 1}$';
outEl.innerHTML = '$q^n = ' + fmt(+qN.toFixed(4)) + ',\\quad S_{' + n + '} = \\mathbf{' + fmt(+sum.toFixed(4)) + '}$';
}
renderMath(termsEl); renderMath(fEl); renderMath(outEl);
if (!bumped){ bumped = true; bumpProgress('p18', 15); addXp(10,'p18-iv1'); }
}
b1Sl.addEventListener('input', redraw);
qSl.addEventListener('input', redraw);
nSl.addEventListener('input', redraw);
redraw();
})();
/* ===== IV2 wiring ===== */
(function(){
const out = document.getElementById('p18-iv2-out');
let bumped = false;
document.getElementById('p18-iv2-go').addEventListener('click', ()=>{
const b1 = +document.getElementById('p18-iv2-b1').value;
const q = +document.getElementById('p18-iv2-q').value;
const n = +document.getElementById('p18-iv2-n').value;
if (!Number.isInteger(n) || n < 1){ out.innerHTML = 'Число членов $n$ должно быть натуральным.'; renderMath(out); return; }
if (b1 === 0){ out.innerHTML = 'Должно быть $b_1 \\ne 0$.'; renderMath(out); return; }
if (q === 0){ out.innerHTML = 'Знаменатель $q$ должен быть $\\ne 0$.'; renderMath(out); return; }
if (Math.abs(q - 1) < 1e-9){
out.innerHTML = '$q = 1$: $S_n = n \\cdot b_1 = ' + n + ' \\cdot ' + b1 + ' = \\mathbf{' + (n*b1) + '}$';
renderMath(out);
} else {
const qN = Math.pow(q, n);
const S = b1 * (qN - 1) / (q - 1);
out.innerHTML = '$q^n = ' + fmt(q) + '^{' + n + '} = ' + fmt(+qN.toFixed(6))
+ '$<br/>$S_n = \\dfrac{b_1(q^n - 1)}{q - 1} = \\dfrac{' + b1 + '\\,(' + fmt(+qN.toFixed(6)) + ' - 1)}{' + fmt(q) + ' - 1} = \\mathbf{' + fmt(+S.toFixed(6)) + '}$';
renderMath(out);
}
if (!bumped){ bumped = true; bumpProgress('p18', 15); addXp(10,'p18-iv2'); }
});
})();
/* ===== IV3 wiring ===== */
(function(){
const items = [
{ q:'$1 + 2 + 4 + 8 = \\dfrac{1 \\cdot (2^4 - 1)}{2 - 1} = 15$',
ans:true, hint:'$b_1 = 1,\\ q = 2,\\ n = 4$: $\\dfrac{16 - 1}{1} = 15$. Верно.' },
{ q:'$1 + 3 + 9 + 27 = \\dfrac{1 \\cdot (3^4 - 1)}{2} = 40$',
ans:true, hint:'$b_1 = 1,\\ q = 3,\\ n = 4$: $\\dfrac{81 - 1}{2} = 40$. Верно.' },
{ q:'Для $q \\ne 1$ верно $S_n = \\dfrac{b_1\\,q^n}{q - 1}$',
ans:false, hint:'Ошибка: в числителе должно быть $q^n - 1$, а не просто $q^n$.' },
{ q:'$5 + 5 + 5 + 5 + 5 = 5 \\cdot 5 = 25$ (при $q = 1$)',
ans:true, hint:'При $q = 1$ формула $S_n = n \\cdot b_1 = 5 \\cdot 5 = 25$. Верно.' },
{ q:'Для $q \\ne 1$ верно $S_3 = b_1 + b_1 q + b_1 q^2 = 3 b_1$',
ans:false, hint:'Сумма равна $3 b_1$ только при $q = 1$. В общем случае это $b_1(1 + q + q^2)$.' },
{ q:'$1 - 2 + 4 - 8 + 16 = \\dfrac{1 \\cdot ((-2)^5 - 1)}{-2 - 1} = \\dfrac{-33}{-3} = 11$',
ans:true, hint:'$b_1 = 1,\\ q = -2,\\ n = 5$: $(-2)^5 = -32$, $\\dfrac{-33}{-3} = 11$. Верно.' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p18-iv3-idx');
const scEl = document.getElementById('p18-iv3-sc');
const qEl = document.getElementById('p18-iv3-q');
const fb = document.getElementById('p18-iv3-fb');
const yBtn = document.getElementById('p18-iv3-y');
const nBtn = document.getElementById('p18-iv3-n');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
yBtn.disabled = true; nBtn.disabled = true;
yBtn.style.opacity = .5; nBtn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p18', 25); addXp(15,'p18-iv3'); }
return;
}
qEl.innerHTML = items[i].q;
fb.style.display = 'none';
renderMath(qEl);
}
function answer(v){
if (i >= items.length) return;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, (ok?'&#10003; Верно. ':'&#10007; Неверно. ') + it.hint);
i++;
setTimeout(render, 1200);
}
yBtn.addEventListener('click', ()=>answer(true));
nBtn.addEventListener('click', ()=>answer(false));
render();
})();
/* ===== IV4 wiring ===== */
(function(){
const items = [
{ q:'Найди $1 + 2 + 4 + 8 + 16$.', ans: 31 },
{ q:'$b_1 = 3,\\ q = 2$. Найти $S_5$.', ans: 93 },
{ q:'Найди $1 + 3 + 9 + 27$.', ans: 40 },
{ q:'$b_1 = 4,\\ q = -1$. Найти $S_5$.', ans: 4 },
{ q:'$b_1 = 8,\\ q = \\dfrac{1}{2}$. Найти $S_4$.', ans: 15 },
{ q:'Геом. прогрессия $1, 4, 16, \\ldots, 256$. Найти сумму всех её членов.', ans: 341 }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p18-iv4-idx');
const scEl = document.getElementById('p18-iv4-sc');
const qEl = document.getElementById('p18-iv4-q');
const inp = document.getElementById('p18-iv4-ans');
const btn = document.getElementById('p18-iv4-go');
const fb = document.getElementById('p18-iv4-fb');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
inp.disabled = true; btn.disabled = true;
inp.style.opacity = .5; btn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p18', 25); addXp(15,'p18-iv4'); }
return;
}
qEl.innerHTML = items[i].q;
inp.value = ''; fb.style.display = 'none';
renderMath(qEl);
}
btn.addEventListener('click', ()=>{
if (i >= items.length) return;
const v = +inp.value;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, ok ? '&#10003; Верно: $'+it.ans+'$' : '&#10007; Неверно. Правильный ответ: $'+it.ans+'$');
i++;
setTimeout(render, 1100);
});
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } });
render();
})();
wireReadBtn('p18');
}
function buildP19(){
const root = document.getElementById('p19-body');
root.innerHTML = `
<div class="card">
<div class="card-header">
<span class="card-icon theory">${ICONS.theory}</span>
<span class="card-title">В разработке</span>
<span class="card-num">§ 19</span>
const box = document.getElementById('p19-body');
let html = '';
html += makeCard('theory', 'Когда прогрессия бесконечно убывающая', '19.1', `
<p>Геометрическая прогрессия называется <b>бесконечно убывающей</b>, если её знаменатель удовлетворяет:</p>
<p>$$|q| < 1\\quad\\text{и}\\quad q \\ne 0.$$</p>
<p>При $|q| < 1$ члены $b_n = b_1 q^{n - 1}$ по модулю <b>стремятся к нулю</b> с ростом $n$.</p>
<p><b>Пример.</b> Прогрессия $1, \\dfrac{1}{2}, \\dfrac{1}{4}, \\dfrac{1}{8}, \\ldots$ имеет $q = \\dfrac{1}{2}$, $|q| < 1$ — бесконечно убывающая.</p>`);
html += makeCard('rule', 'Формула суммы $S$', '19.2', `
<p><b>Сумма всех членов</b> бесконечно убывающей геом. прогрессии:</p>
<p>$$S = \\dfrac{b_1}{1 - q},\\quad |q| < 1.$$</p>
<p><b>Откуда это?</b> Это <b>предел</b> $S_n$ при $n \\to \\infty$. Действительно,</p>
<p>$$S_n = \\dfrac{b_1\\,(q^n - 1)}{q - 1}.$$</p>
<p>При $|q| < 1$ имеем $q^n \\to 0$, поэтому</p>
<p>$$S = \\lim_{n \\to \\infty} S_n = \\dfrac{b_1 \\cdot (0 - 1)}{q - 1} = \\dfrac{-b_1}{q - 1} = \\dfrac{b_1}{1 - q}.$$</p>`);
html += makeCard('example', 'Примеры', '19.3', `
<p><b>а)</b> $1 + \\dfrac{1}{2} + \\dfrac{1}{4} + \\dfrac{1}{8} + \\ldots = \\dfrac{1}{1 - \\tfrac{1}{2}} = \\mathbf{2}.$</p>
<p><b>б)</b> $1 - \\dfrac{1}{3} + \\dfrac{1}{9} - \\dfrac{1}{27} + \\ldots = \\dfrac{1}{1 - (-\\tfrac{1}{3})} = \\dfrac{1}{\\tfrac{4}{3}} = \\mathbf{\\dfrac{3}{4}}.$</p>
<p><b>в) Знаменитое равенство $0{,}(9) = 1$.</b> Действительно:</p>
<p>$$0{,}999\\ldots = 0{,}9 + 0{,}09 + 0{,}009 + \\ldots = \\dfrac{0{,}9}{1 - 0{,}1} = \\dfrac{0{,}9}{0{,}9} = \\mathbf{1}.$$</p>
<p><b>г) Превращение периодической дроби в обыкновенную:</b></p>
<p>$$0{,}(3) = 0{,}3 + 0{,}03 + 0{,}003 + \\ldots = \\dfrac{0{,}3}{1 - 0{,}1} = \\dfrac{0{,}3}{0{,}9} = \\mathbf{\\dfrac{1}{3}}.$$</p>`);
/* INTERACTIVE 1 — визуализация сходимости */
html += `<div class="wg" id="p19-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Визуализация сходимости</div></div>
<div class="wg-help">Двигай $b_1$ и $q$ (при $|q| < 1$). На диаграмме — столбики первых 15 членов и нарастающая сумма, которая стремится к $S = \\dfrac{b_1}{1 - q}$.</div>
<div class="sliders">
<label>$b_1$ =<b id="p19-iv1-bv">4</b><input type="range" id="p19-iv1-b1" min="-10" max="10" step="1" value="4"></label>
<label>$q$ =<b id="p19-iv1-qv">0.5</b><input type="range" id="p19-iv1-q" min="-0.95" max="0.95" step="0.05" value="0.5"></label>
</div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p19-iv1-svg" viewBox="0 0 480 280" style="width:100%;max-width:600px;height:auto;display:block;margin:0 auto"></svg>
</div>
<div id="p19-iv1-formula" style="text-align:center;font-size:1.05rem;padding:10px;background:var(--card);border-radius:9px;margin-top:8px"></div>
<div id="p19-iv1-out" style="margin-top:8px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem"></div>
</div>`;
/* INTERACTIVE 2 — калькулятор S */
html += `<div class="wg" id="p19-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор $S = \\dfrac{b_1}{1 - q}$</div></div>
<div class="wg-help">$q$ задаётся обыкновенной дробью — введи числитель и знаменатель отдельно.</div>
<div style="background:var(--card);border-radius:11px;padding:14px;max-width:380px;margin:0 auto 10px">
<div style="display:grid;grid-template-columns:auto 1fr;gap:8px 10px;align-items:center;margin-bottom:10px">
<span>$b_1$ =</span><input type="number" id="p19-iv2-b1" class="tinp" value="1" step="1">
<span>$q$ числитель =</span><input type="number" id="p19-iv2-qn" class="tinp" value="1" step="1">
<span>$q$ знаменатель =</span><input type="number" id="p19-iv2-qd" class="tinp" value="2" step="1">
</div>
<div class="card-body">
<p>Содержание параграфа <b>«Бесконечно убывающая»</b> будет добавлено в следующих обновлениях.</p>
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 1.</p>
</div>
</div>` + secNav('p18', 'final4') + readButton('p19');
renderMath(root);
<button class="btn primary" id="p19-iv2-go" style="width:100%">Найти $S$</button>
</div>
<div id="p19-iv2-out" style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:8px;font-size:.95rem;min-height:40px;text-align:center"></div>
</div>`;
/* INTERACTIVE 3 — можно ли применить формулу */
html += `<div class="wg" id="p19-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Можно ли применить формулу $\\dfrac{b_1}{1 - q}$?</div></div>
<div class="wg-help">Формула работает только при $|q| < 1$ и $q \\ne 0$. Реши, подходит ли данная прогрессия.</div>
<div class="score-display">Задача: <b id="p19-iv3-idx">1</b> / 6 &middot; Очки: <b id="p19-iv3-sc">0</b></div>
<div id="p19-iv3-q" style="text-align:center;font-size:1.0rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p19-iv3-y">Да, $|q| < 1$</button>
<button class="btn" id="p19-iv3-n">Нет</button>
</div>
<div class="feedback" id="p19-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — тренажёр */
html += `<div class="wg" id="p19-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр сумм $S = \\dfrac{b_1}{1 - q}$</div></div>
<div class="wg-help">В каждой задаче указано, что вводить (целое значение, или числитель / знаменатель ответа).</div>
<div class="score-display">Задача: <b id="p19-iv4-idx">1</b> / 6 &middot; Очки: <b id="p19-iv4-sc">0</b></div>
<div id="p19-iv4-q" style="text-align:center;font-size:1.0rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center;align-items:center">
<span style="font-family:'JetBrains Mono',monospace">Ответ:</span>
<input type="number" id="p19-iv4-ans" class="tinp" style="width:120px;text-align:center" step="1">
<button class="btn primary" id="p19-iv4-go">Проверить</button>
</div>
<div class="feedback" id="p19-iv4-fb"></div>
</div>`;
box.innerHTML = html + secNav('p18', 'final4') + readButton('p19');
renderMath(box);
/* ===== IV1 wiring — визуализация ===== */
(function(){
const svg = document.getElementById('p19-iv1-svg');
const b1Sl = document.getElementById('p19-iv1-b1');
const qSl = document.getElementById('p19-iv1-q');
const bv = document.getElementById('p19-iv1-bv');
const qv = document.getElementById('p19-iv1-qv');
const fEl = document.getElementById('p19-iv1-formula');
const outEl = document.getElementById('p19-iv1-out');
let bumped = false;
function redraw(){
const b1 = +b1Sl.value, q = +qSl.value;
bv.textContent = b1; qv.textContent = q.toFixed(2);
const N = 15;
const terms = [];
const partial = [];
let s = 0;
for (let i = 0; i < N; i++){
const t = b1 * Math.pow(q, i);
terms.push(t); s += t; partial.push(s);
}
const Sinf = (Math.abs(q) < 1 - 1e-9) ? b1 / (1 - q) : NaN;
// SVG: верх — столбики членов, низ — частичные суммы
const W = 480, H = 280, pad = 22;
const barAreaH = 130;
const partAreaTop = 160;
const partAreaH = 100;
const ux = (W - 2*pad) / N;
// вертикальный масштаб для членов
const tmax = Math.max(...terms.map(Math.abs), 1e-6);
const tScale = (barAreaH / 2 - 6) / tmax;
const tMid = pad + barAreaH / 2;
// масштаб для частичных
const allP = partial.concat([Sinf||0, 0]);
let pMin = Math.min(...allP), pMax = Math.max(...allP);
if (pMax === pMin){ pMin -= 1; pMax += 1; }
const padP = (pMax - pMin) * 0.15;
pMin -= padP; pMax += padP;
const pScale = (partAreaH - 12) / (pMax - pMin);
const pToY = v => partAreaTop + 6 + (pMax - v) * pScale;
let g = '';
// фон областей
g += '<rect x="'+pad+'" y="'+pad+'" width="'+(W-2*pad)+'" height="'+barAreaH+'" fill="#f8fafc" stroke="#e5e7eb"/>';
g += '<rect x="'+pad+'" y="'+partAreaTop+'" width="'+(W-2*pad)+'" height="'+partAreaH+'" fill="#f8fafc" stroke="#e5e7eb"/>';
// средняя линия для членов (ось 0)
g += '<line x1="'+pad+'" y1="'+tMid+'" x2="'+(W-pad)+'" y2="'+tMid+'" stroke="#94a3b8" stroke-width="1"/>';
g += '<text x="'+(pad+4)+'" y="'+(pad+12)+'" font-size="10" fill="#64748b">Члены $b_n$</text>';
g += '<text x="'+(pad+4)+'" y="'+(partAreaTop+12)+'" font-size="10" fill="#64748b">Частичные суммы $S_n$</text>';
// столбики членов
const barW = ux * 0.7;
for (let i = 0; i < N; i++){
const x = pad + ux * (i + 0.15);
const h = Math.abs(terms[i]) * tScale;
const y = terms[i] >= 0 ? tMid - h : tMid;
const color = terms[i] >= 0 ? '#0891b2' : '#ef4444';
g += '<rect x="'+x.toFixed(1)+'" y="'+y.toFixed(1)+'" width="'+barW.toFixed(1)+'" height="'+h.toFixed(1)+'" fill="'+color+'" opacity=".75"/>';
}
// линия Sinf
if (isFinite(Sinf)){
const ySinf = pToY(Sinf);
g += '<line x1="'+pad+'" y1="'+ySinf+'" x2="'+(W-pad)+'" y2="'+ySinf+'" stroke="#16a34a" stroke-width="1.5" stroke-dasharray="5 3"/>';
g += '<text x="'+(W-pad-4)+'" y="'+(ySinf-4)+'" font-size="10" fill="#16a34a" text-anchor="end">$S = '+fmt(+Sinf.toFixed(4))+'$</text>';
}
// линия 0 в нижней области
const yZero = pToY(0);
if (yZero >= partAreaTop && yZero <= partAreaTop + partAreaH){
g += '<line x1="'+pad+'" y1="'+yZero+'" x2="'+(W-pad)+'" y2="'+yZero+'" stroke="#94a3b8" stroke-width="1"/>';
}
// точки и ломаная частичных сумм
let path = '';
for (let i = 0; i < N; i++){
const x = pad + ux * (i + 0.5);
const y = pToY(partial[i]);
path += (i === 0 ? 'M' : ' L') + x.toFixed(1) + ',' + y.toFixed(1);
}
g += '<path d="'+path+'" stroke="#7c3aed" stroke-width="2" fill="none"/>';
for (let i = 0; i < N; i++){
const x = pad + ux * (i + 0.5);
const y = pToY(partial[i]);
g += '<circle cx="'+x.toFixed(1)+'" cy="'+y.toFixed(1)+'" r="3" fill="#7c3aed"/>';
}
svg.innerHTML = g;
// формула
if (isFinite(Sinf)){
fEl.innerHTML = '$S = \\dfrac{b_1}{1 - q} = \\dfrac{' + b1 + '}{1 - (' + q.toFixed(2) + ')} = \\mathbf{' + fmt(+Sinf.toFixed(4)) + '}$';
// частичные S_1..S_10
const ps = partial.slice(0, 10).map((s,i)=>'$S_{'+(i+1)+'}\\!=\\!'+fmt(+s.toFixed(3))+'$').join(',\\ ');
outEl.innerHTML = '<b>Частичные суммы:</b> ' + ps + ' &mdash; стремятся к $S$.';
} else {
fEl.innerHTML = '<b>$|q| \\ge 1$:</b> прогрессия не является бесконечно убывающей.';
outEl.innerHTML = '<b>Частичные суммы расходятся</b> — формула $\\dfrac{b_1}{1 - q}$ не применима.';
}
renderMath(fEl); renderMath(outEl);
if (!bumped){ bumped = true; bumpProgress('p19', 15); addXp(10,'p19-iv1'); }
}
b1Sl.addEventListener('input', redraw);
qSl.addEventListener('input', redraw);
redraw();
})();
/* ===== IV2 wiring ===== */
(function(){
const out = document.getElementById('p19-iv2-out');
let bumped = false;
document.getElementById('p19-iv2-go').addEventListener('click', ()=>{
const b1 = +document.getElementById('p19-iv2-b1').value;
const qn = +document.getElementById('p19-iv2-qn').value;
const qd = +document.getElementById('p19-iv2-qd').value;
if (qd === 0){ out.innerHTML = 'Знаменатель $q$ не может быть нулём.'; renderMath(out); return; }
if (b1 === 0){ out.innerHTML = 'Должно быть $b_1 \\ne 0$.'; renderMath(out); return; }
const q = qn / qd;
if (q === 0){ out.innerHTML = '$q$ должен быть отличен от $0$.'; renderMath(out); return; }
if (Math.abs(q) >= 1){
out.innerHTML = '$q = \\dfrac{'+qn+'}{'+qd+'} = '+fmt(q)+'$. <b>$|q| \\ge 1$</b> — прогрессия не бесконечно убывающая!';
renderMath(out); return;
}
// S = b1 / (1 - q) = b1 * qd / (qd - qn)
const num = b1 * qd;
const den = qd - qn;
const g = gcd(num, den);
const sNum = num / g, sDen = den / g;
// знак в числитель
let nn = sNum, dd = sDen;
if (dd < 0){ nn = -nn; dd = -dd; }
const S = b1 / (1 - q);
let frac;
if (dd === 1) frac = '' + nn;
else frac = '\\dfrac{' + nn + '}{' + dd + '}';
out.innerHTML = '$q = \\dfrac{'+qn+'}{'+qd+'},\\ |q| < 1$ &nbsp;&#10003;<br/>$S = \\dfrac{b_1}{1 - q} = \\dfrac{'+b1+'}{1 - \\tfrac{'+qn+'}{'+qd+'}} = \\dfrac{'+b1+' \\cdot '+qd+'}{'+qd+' - ('+qn+')} = \\mathbf{'+frac+'} \\approx '+fmt(+S.toFixed(4))+'$';
renderMath(out);
if (!bumped){ bumped = true; bumpProgress('p19', 15); addXp(10,'p19-iv2'); }
});
})();
/* ===== IV3 wiring ===== */
(function(){
const items = [
{ q:'$b_1 = 1,\\ q = \\dfrac{1}{2}$', ans:true, hint:'$|q| = \\tfrac{1}{2} < 1$ — формула применима.' },
{ q:'$b_1 = 1,\\ q = 2$', ans:false, hint:'$|q| = 2 > 1$ — прогрессия расходится, формула неприменима.' },
{ q:'$b_1 = 5,\\ q = -\\dfrac{1}{3}$', ans:true, hint:'$|q| = \\tfrac{1}{3} < 1$ — формула применима.' },
{ q:'$b_1 = 100,\\ q = 0{,}99$', ans:true, hint:'$|q| = 0{,}99 < 1$ — формула применима (сходимость медленная, но есть).' },
{ q:'$b_1 = 1,\\ q = -1$', ans:false, hint:'$|q| = 1$ — граница, формула неприменима ($1 - 1 + 1 - 1 + \\ldots$ не сходится).' },
{ q:'$b_1 = 1,\\ q = 1$', ans:false, hint:'$|q| = 1$ — это вообще не убывающая прогрессия ($1 + 1 + 1 + \\ldots = \\infty$).' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p19-iv3-idx');
const scEl = document.getElementById('p19-iv3-sc');
const qEl = document.getElementById('p19-iv3-q');
const fb = document.getElementById('p19-iv3-fb');
const yBtn = document.getElementById('p19-iv3-y');
const nBtn = document.getElementById('p19-iv3-n');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
yBtn.disabled = true; nBtn.disabled = true;
yBtn.style.opacity = .5; nBtn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p19', 25); addXp(15,'p19-iv3'); }
return;
}
qEl.innerHTML = items[i].q;
fb.style.display = 'none';
renderMath(qEl);
}
function answer(v){
if (i >= items.length) return;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, (ok?'&#10003; Верно. ':'&#10007; Неверно. ') + it.hint);
i++;
setTimeout(render, 1200);
}
yBtn.addEventListener('click', ()=>answer(true));
nBtn.addEventListener('click', ()=>answer(false));
render();
})();
/* ===== IV4 wiring ===== */
(function(){
const items = [
{ q:'$1 + \\dfrac{1}{2} + \\dfrac{1}{4} + \\ldots = \\ ?$ Введи целое значение $S$.', ans: 2, expl:'$S = \\dfrac{1}{1 - 1/2} = 2$.' },
{ q:'$4 + 2 + 1 + \\dfrac{1}{2} + \\ldots = \\ ?$ Введи целое значение $S$.', ans: 8, expl:'$b_1 = 4,\\ q = \\tfrac{1}{2}$: $S = \\dfrac{4}{1 - 1/2} = 8$.' },
{ q:'$1 - \\dfrac{1}{2} + \\dfrac{1}{4} - \\ldots = \\dfrac{2}{?}$ Введи знаменатель.', ans: 3, expl:'$S = \\dfrac{1}{1 + 1/2} = \\dfrac{2}{3}$.' },
{ q:'$0{,}(6) = 0{,}666\\ldots = \\dfrac{2}{?}$ Введи знаменатель.', ans: 3, expl:'$S = \\dfrac{0{,}6}{1 - 0{,}1} = \\dfrac{0{,}6}{0{,}9} = \\dfrac{2}{3}$.' },
{ q:'$9 + 3 + 1 + \\dfrac{1}{3} + \\ldots = \\dfrac{?}{2}$ Введи числитель.', ans: 27, expl:'$S = \\dfrac{9}{1 - 1/3} = \\dfrac{9 \\cdot 3}{2} = \\dfrac{27}{2}$.' },
{ q:'$0{,}(12) = 0{,}1212\\ldots = \\dfrac{4}{?}$ Введи знаменатель.', ans: 33, expl:'$S = \\dfrac{0{,}12}{1 - 0{,}01} = \\dfrac{12}{99} = \\dfrac{4}{33}$.' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p19-iv4-idx');
const scEl = document.getElementById('p19-iv4-sc');
const qEl = document.getElementById('p19-iv4-q');
const inp = document.getElementById('p19-iv4-ans');
const btn = document.getElementById('p19-iv4-go');
const fb = document.getElementById('p19-iv4-fb');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
inp.disabled = true; btn.disabled = true;
inp.style.opacity = .5; btn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p19', 25); addXp(15,'p19-iv4'); }
return;
}
qEl.innerHTML = items[i].q;
inp.value = ''; fb.style.display = 'none';
renderMath(qEl);
}
btn.addEventListener('click', ()=>{
if (i >= items.length) return;
const v = +inp.value;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, (ok ? '&#10003; Верно: $'+it.ans+'$. ' : '&#10007; Неверно. Правильный ответ: $'+it.ans+'$. ') + it.expl);
i++;
setTimeout(render, 1300);
});
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } });
render();
})();
wireReadBtn('p19');
}