From eb565081f6da5d294ce51955cee83a31fbb3cdce Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 29 May 2026 08:51:52 +0300 Subject: [PATCH] =?UTF-8?q?feat(alg9=20ch4=20wave1):=20=C2=A714=20=C2=AB?= =?UTF-8?q?=D0=A7=D0=B8=D1=81=D0=BB=D0=BE=D0=B2=D0=B0=D1=8F=20=D0=BF=D0=BE?= =?UTF-8?q?=D1=81=D0=BB=D0=B5=D0=B4=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D0=BE=D1=81=D1=82=D1=8C=C2=BB=20+=20=C2=A715=20?= =?UTF-8?q?=C2=AB=D0=90=D1=80=D0=B8=D1=84=D0=BC.=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B3=D1=80=D0=B5=D1=81=D1=81=D0=B8=D1=8F=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §14: 3 теор. карточки (определение, способы задания, монотонность) + 4 интерактива — конструктор последовательности (5 типов + точечная диаграмма (n, a_n)), тренажёр вычисления a_n (6 задач), квикфайр монотонности (3 кнопки), DnD-сортер «рекуррентно vs формула». §15: 3 теор. карточки (определение и a_n = a_1 + (n-1)d, характеристическое свойство, примеры) + 4 интерактива — конструктор арифм. прогрессии (слайдеры a_1 и d + точки на прямой), двойной калькулятор a_n и d, квикфайр «арифм. или нет» (6 заданий), тренажёр прогрессии (6 задач). Добавлены CSS .wg/.tinp/.sliders/.score-display/.dnd-*/.drop-* и хелперы makeCard, setupSorter, gcd, axes2D, plotFunc (по образцу ch1/ch2). --- frontend/textbooks/algebra_9_ch4.html | 691 +++++++++++++++++++++++++- 1 file changed, 665 insertions(+), 26 deletions(-) diff --git a/frontend/textbooks/algebra_9_ch4.html b/frontend/textbooks/algebra_9_ch4.html index bde041b..76df4ec 100644 --- a/frontend/textbooks/algebra_9_ch4.html +++ b/frontend/textbooks/algebra_9_ch4.html @@ -111,6 +111,40 @@ a{color:inherit;text-decoration:none} .feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)} .feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)} +.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1} +.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px} +.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em} +.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1} +.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px} +.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace} +.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))} +.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px} +.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px} +.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5} +.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2));margin-left:4px} +.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--sec-acc,var(--pri))} +.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px} +.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem} +.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s} +.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid} +.dnd-pool.col{flex-direction:column;align-items:stretch} +.dnd-pool.col .dnd-chip{width:auto} +.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%} +.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)} +.dnd-chip:active{cursor:grabbing} +.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(217,119,6,.22);transform:translateY(-1px)} +.dnd-chip.dragging{opacity:.28} +.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))} +.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer} +.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)} +.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s} +.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))} +.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em} +.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)} +.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px} +.dnd-hint{font-size:.78rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px} +.dnd-hint svg{width:14px;height:14px;flex-shrink:0} + .col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto} .sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)} .sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)} @@ -442,41 +476,646 @@ function wireReadBtn(paraId){ }); } +function gcd(a,b){ a=Math.abs(a|0); b=Math.abs(b|0); while(b){ const t=b; b=a%b; a=t; } return a||1; } +function makeCard(kind, title, num, body){ + const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно'}; + return '
'+ICONS[kind]+'
'+(labels[kind]||'')+(title&&title!==labels[kind]?' \xb7 '+title:'')+'
'+(num?'
'+num+'
':'')+'
'+body+'
'; +} +function setupSorter(cfg){ + const placed = {}; const pool = document.getElementById(cfg.poolId); const scope = document.querySelector(cfg.scopeSelector); + if(!pool||!scope) return {placed,render:()=>{},reset:()=>{}}; + pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col'); + let armed = null; + function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML=''+it.html+'\xd7'; attach(e,it.id); return e; } + function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); } + function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); } + function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} } + attachBoxTaps(); render(); + return {placed,render,reset(){ for(const k in placed)delete placed[k];armed=null;render(); }}; +} + +/* Координатная плоскость в SVG */ +function axes2D(W, H, pad, xmin, xmax, ymin, ymax){ + const ux = (W - 2*pad) / (xmax - xmin); + const uy = (H - 2*pad) / (ymax - ymin); + const toX = v => pad + (v - xmin) * ux; + const toY = v => H - pad - (v - ymin) * uy; + let g = ''; + g += ''; + for (let x = Math.ceil(xmin); x <= xmax; x++){ + g += ''; + } + for (let y = Math.ceil(ymin); y <= ymax; y++){ + g += ''; + } + g += ''; + const y0 = toY(0), x0 = toX(0); + g += ''; + g += ''; + g += 'x'; + g += 'y'; + g += ''; + for (let x = Math.ceil(xmin); x <= xmax; x++){ + if (x !== 0) g += ''+x+''; + } + for (let y = Math.ceil(ymin); y <= ymax; y++){ + if (y !== 0) g += ''+y+''; + } + g += '0'; + g += ''; + return { content: g, toX, toY, ux, uy }; +} + +/* График функции y=f(x) — возвращает строку */ +function plotFunc(f, xmin, xmax, toX, toY, color, N){ + N = N || 200; + let d = ''; + let prevValid = false; + for (let i = 0; i <= N; i++){ + const x = xmin + (xmax - xmin) * i / N; + let y; + try { y = f(x); } catch(e){ y = NaN; } + if (!isFinite(y) || isNaN(y) || y < -1e4 || y > 1e4){ prevValid = false; continue; } + d += (prevValid ? ' L' : ' M') + toX(x).toFixed(2) + ',' + toY(y).toFixed(2); + prevValid = true; + } + return ''; +} + /* ===== STUB BUILDERS — наполнение в Phase 1+ ===== */ function buildP14(){ - const root = document.getElementById('p14-body'); - root.innerHTML = ` -
-
- ${ICONS.theory} - В разработке - § 14 -
-
-

Содержание параграфа «Числовая последовательность» будет добавлено в следующих обновлениях.

-

Раздел Phase 1.

-
-
` + secNav(null, 'p15') + readButton('p14'); - renderMath(root); + const box = document.getElementById('p14-body'); + let html = ''; + + html += makeCard('theory', 'Определение', '14.1', ` +

Числовая последовательность — это функция, заданная на множестве натуральных чисел $\\mathbb{N}$.

+

Записывают: $a_1,\\ a_2,\\ a_3,\\ \\ldots,\\ a_n,\\ \\ldots$ — это члены последовательности.

+
    +
  • $a_n$ — общий член (член с номером $n$);
  • +
  • $n$ — номер члена ($n \\in \\mathbb{N}$);
  • +
  • $a_1$ — первый член, $a_2$ — второй, и т. д.
  • +
+

Кратко последовательность обозначают $(a_n)$. Это значит: $a \\colon \\mathbb{N} \\to \\mathbb{R}$.

`); + + html += makeCard('rule', 'Три способа задания', '14.2', ` +

Последовательность можно задать одним из трёх способов:

+
    +
  1. Формулой $n$-го члена: $a_n = 2n + 1$ → последовательность $3, 5, 7, 9, \\ldots$ Чтобы найти любой член, подставь его номер $n$.
  2. +
  3. Рекуррентно: задают $a_1$ и формулу, выражающую следующий член через предыдущий. Например: $a_1 = 1,\\ a_{n+1} = a_n + 3$ → $1, 4, 7, 10, \\ldots$
  4. +
  5. Перечислением (словесно): $2, 4, 8, 16, \\ldots$ — если из записи понятен алгоритм (удвоение).
  6. +
`); + + html += makeCard('example', 'Конечные/бесконечные · возрастающие/убывающие', '14.3', ` +

Конечная последовательность: $1, 2, 3, \\ldots, 100$ — заканчивается на $a_{100}$.

+

Бесконечная: $1, 2, 4, 8, \\ldots$ — продолжается без конца.

+

Последовательность $(a_n)$ — возрастающая, если $a_{n+1} > a_n$ для всех $n$.
+ Убывающая — если $a_{n+1} < a_n$ для всех $n$.

+

Примеры:

+
    +
  • $a_n = -n$ → $-1, -2, -3, \\ldots$ — убывающая;
  • +
  • $a_n = \\dfrac{1}{n}$ → $1, \\dfrac{1}{2}, \\dfrac{1}{3}, \\ldots$ — убывающая, но положительная;
  • +
  • $a_n = n^2$ → $1, 4, 9, 16, \\ldots$ — возрастающая;
  • +
  • $a_n = (-1)^n$ → $-1, 1, -1, 1, \\ldots$ — ни возрастает, ни убывает.
  • +
`); + + /* INTERACTIVE 1 — конструктор последовательности */ + html += `
+
ИНТЕРАКТИВ 1
Конструктор последовательности
+
Выбери одну из пяти классических последовательностей и посмотри её точечную диаграмму $(n, a_n)$ на координатной плоскости.
+
+ +
+
+
+ +
+
+
+
`; + + /* INTERACTIVE 2 — вычисли a_n */ + html += `
+
ИНТЕРАКТИВ 2
Вычисли $a_n$
+
Подставь номер $n$ в формулу и вычисли значение $a_n$. Ответ — целое число.
+
Задача: 1 / 6 · Очки: 0
+
+
+ $a_n$ = + + +
+ +
`; + + /* INTERACTIVE 3 — возрастает/убывает/нет */ + html += `
+
ИНТЕРАКТИВ 3
Возрастает или убывает?
+
Дана формула $a_n$. Определи: возрастает, убывает или ни то ни другое.
+
Задача: 1 / 6 · Очки: 0
+
+
+ + + +
+ +
`; + + /* INTERACTIVE 4 — сортер: рекуррентно или формулой */ + html += `
+
ИНТЕРАКТИВ 4
Рекуррентно или формулой?
+
Перетащи каждый способ задания в нужный ящик.
+
+
+
Рекуррентное правило
+
Формула $n$-го члена
+
+
+ +
`; + + box.innerHTML = html + secNav(null, 'p15') + readButton('p14'); + renderMath(box); + + /* ===== IV1 wiring ===== */ + (function(){ + const types = [ + { tex:'a_n = 2n - 1', f:n=>2*n-1, seq:'1, 3, 5, 7, 9, \\ldots', mono:'возрастает' }, + { tex:'a_n = n^2', f:n=>n*n, seq:'1, 4, 9, 16, 25, \\ldots', mono:'возрастает' }, + { tex:'a_n = (-1)^n', f:n=>Math.pow(-1,n), seq:'-1, 1, -1, 1, \\ldots', mono:'ни возрастает, ни убывает' }, + { tex:'a_n = \\dfrac{1}{n}', f:n=>1/n, seq:'1, \\dfrac{1}{2}, \\dfrac{1}{3}, \\dfrac{1}{4}, \\ldots', mono:'убывает' }, + { tex:'a_n = n(n+1)', f:n=>n*(n+1), seq:'2, 6, 12, 20, 30, \\ldots', mono:'возрастает' } + ]; + const svg = document.getElementById('p14-iv1-svg'); + const fnSl = document.getElementById('p14-iv1-fn'); + const ti = document.getElementById('p14-iv1-ti'); + const formula = document.getElementById('p14-iv1-formula'); + const out = document.getElementById('p14-iv1-out'); + const mono = document.getElementById('p14-iv1-mono'); + let bumped = false; + + function redraw(){ + const idx = (+fnSl.value)-1; + const t = types[idx]; + ti.textContent = (idx+1); + + // вычисляем 10 точек + const pts = []; + for (let n=1;n<=10;n++){ const y=t.f(n); if (isFinite(y)) pts.push([n,y]); } + let ymin = Math.min(...pts.map(p=>p[1]), 0); + let ymax = Math.max(...pts.map(p=>p[1]), 0); + // ограничим, чтобы график не «улетал» + if (ymax > 30) ymax = 30; + if (ymin < -5) ymin = -5; + const pad = Math.max(1, (ymax-ymin)*0.1); + ymin = Math.floor(ymin - pad); + ymax = Math.ceil(ymax + pad); + + const ax = axes2D(420, 280, 32, 0, 11, ymin, ymax); + let g = ax.content; + // точки + соединяющие отрезки (пунктир) + let path = ''; + pts.forEach((p,i)=>{ + const x = ax.toX(p[0]); const y = ax.toY(Math.min(ymax,Math.max(ymin,p[1]))); + path += (i===0?'M':' L')+x.toFixed(1)+','+y.toFixed(1); + }); + g += ''; + pts.forEach(p=>{ + const x = ax.toX(p[0]); const y = ax.toY(Math.min(ymax,Math.max(ymin,p[1]))); + g += ''; + }); + svg.innerHTML = g; + + formula.innerHTML = '$' + t.tex + '$'; + out.innerHTML = 'Первые 10 членов: $' + t.seq + '$'; + mono.innerHTML = 'Эта последовательность ' + t.mono + ''; + renderMath(formula); renderMath(out); renderMath(mono); + if (!bumped){ bumped = true; bumpProgress('p14', 15); addXp(10,'p14-iv1'); } + } + fnSl.addEventListener('input', redraw); + redraw(); + })(); + + /* ===== IV2 wiring ===== */ + (function(){ + const items = [ + { q:'$a_n = 3n + 1$, найти $a_5$', ans: 16 }, + { q:'$a_n = n^2 - 2$, найти $a_4$', ans: 14 }, + { q:'$a_n = 2^n$, найти $a_6$', ans: 64 }, + { q:'$a_n = \\dfrac{6}{n}$, найти $a_3$', ans: 2 }, + { q:'$a_n = (-1)^n \\cdot n$, найти $a_7$', ans: -7 }, + { q:'$a_n = n^3 - n$, найти $a_3$', ans: 24 } + ]; + let i = 0, sc = 0; + const idxEl = document.getElementById('p14-iv2-idx'); + const scEl = document.getElementById('p14-iv2-sc'); + const qEl = document.getElementById('p14-iv2-q'); + const inp = document.getElementById('p14-iv2-ans'); + const btn = document.getElementById('p14-iv2-go'); + const fb = document.getElementById('p14-iv2-fb'); + let bumped = false; + function render(){ + idxEl.textContent = Math.min(i+1, items.length); + scEl.textContent = sc; + if (i >= items.length){ + qEl.innerHTML = 'Готово! Результат: ' + sc + ' / ' + items.length; + inp.disabled = true; btn.disabled = true; + inp.style.opacity = .5; btn.style.opacity = .5; + if (!bumped){ bumped = true; bumpProgress('p14', 15); addXp(10,'p14-iv2'); } + 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 ? '✓ Верно. $a_n = ' + it.ans + '$' : '✗ Неверно. Правильный ответ: $a_n = ' + it.ans + '$'); + i++; + setTimeout(render, 1000); + }); + inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } }); + render(); + })(); + + /* ===== IV3 wiring ===== */ + (function(){ + const items = [ + { q:'$a_n = 2n$', ans:'up', hint:'$a_{n+1} - a_n = 2 > 0$.' }, + { q:'$a_n = 10 - n$', ans:'dn', hint:'$a_{n+1} - a_n = -1 < 0$.' }, + { q:'$a_n = (-1)^n$', ans:'no', hint:'Чередуется $-1, 1, -1, 1, \\ldots$' }, + { q:'$a_n = n^2$', ans:'up', hint:'$1, 4, 9, 16, \\ldots$ — растёт.' }, + { q:'$a_n = \\dfrac{1}{n}$', ans:'dn', hint:'$1, \\tfrac{1}{2}, \\tfrac{1}{3}, \\ldots$ — убывает.' }, + { q:'$a_n = n - n^2$', ans:'dn', hint:'$0, -2, -6, -12, \\ldots$ — убывает с $n=1$.' } + ]; + let i = 0, sc = 0; + const idxEl = document.getElementById('p14-iv3-idx'); + const scEl = document.getElementById('p14-iv3-sc'); + const qEl = document.getElementById('p14-iv3-q'); + const fb = document.getElementById('p14-iv3-fb'); + const upB = document.getElementById('p14-iv3-up'); + const dnB = document.getElementById('p14-iv3-dn'); + const noB = document.getElementById('p14-iv3-no'); + let bumped = false; + function render(){ + idxEl.textContent = Math.min(i+1, items.length); + scEl.textContent = sc; + if (i >= items.length){ + qEl.innerHTML = 'Готово! Результат: ' + sc + ' / ' + items.length; + [upB,dnB,noB].forEach(b=>{ b.disabled=true; b.style.opacity=.5; }); + if (!bumped){ bumped = true; bumpProgress('p14', 25); addXp(15,'p14-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?'✓ Верно. ':'✗ Неверно. ') + it.hint); + i++; + setTimeout(render, 1000); + } + upB.addEventListener('click', ()=>answer('up')); + dnB.addEventListener('click', ()=>answer('dn')); + noB.addEventListener('click', ()=>answer('no')); + render(); + })(); + + /* ===== IV4 wiring — DnD ===== */ + (function(){ + const items = [ + { id:'a', html:'$a_n = 5n - 3$', cat:'fml' }, + { id:'b', html:'$a_1 = 2,\\ a_{n+1} = a_n + 5$', cat:'rec' }, + { id:'c', html:'$a_n = n^2$', cat:'fml' }, + { id:'d', html:'$a_1 = 1,\\ a_{n+1} = 2 a_n$', cat:'rec' }, + { id:'e', html:'$a_n = \\dfrac{n}{n+1}$', cat:'fml' }, + { id:'f', html:'$a_1 = 3,\\ a_{n+1} = a_n - 2$', cat:'rec' } + ]; + const sorter = setupSorter({ + poolId: 'p14-iv4-pool', + scopeSelector: '#p14-iv4', + cats: ['rec','fml'], + items: items + }); + let bumped = false; + document.getElementById('p14-iv4-check').addEventListener('click', ()=>{ + const fb = document.getElementById('p14-iv4-fb'); + const total = items.length; + let correct = 0, placed = 0; + items.forEach(it=>{ if(sorter.placed[it.id]){ placed++; if(sorter.placed[it.id]===it.cat) correct++; } }); + if (placed < total){ + feedback(fb, false, 'Размещены не все: ' + placed + ' / ' + total + '.'); + return; + } + const ok = (correct === total); + feedback(fb, ok, ok ? '✓ Все верно! ' + correct + ' / ' + total : '✗ Правильно: ' + correct + ' / ' + total); + if (ok && !bumped){ bumped = true; bumpProgress('p14', 25); addXp(15,'p14-iv4'); } + }); + document.getElementById('p14-iv4-reset').addEventListener('click', ()=>{ + sorter.reset(); + const fb = document.getElementById('p14-iv4-fb'); fb.style.display='none'; + }); + })(); + wireReadBtn('p14'); } function buildP15(){ - const root = document.getElementById('p15-body'); - root.innerHTML = ` -
-
- ${ICONS.theory} - В разработке - § 15 + const box = document.getElementById('p15-body'); + let html = ''; + + html += makeCard('theory', 'Определение и формула $n$-го члена', '15.1', ` +

Арифметическая прогрессия — числовая последовательность, в которой каждый следующий член отличается от предыдущего на одно и то же число $d$ — разность прогрессии.

+

$$a_{n+1} - a_n = d \\quad \\text{для всех } n \\in \\mathbb{N}.$$

+

Формула $n$-го члена:

+

$$a_n = a_1 + (n - 1)\\, d.$$

+

Пример. $5, 8, 11, 14, \\ldots$ — арифметическая прогрессия с $a_1 = 5$, $d = 3$. Тогда + $a_{10} = 5 + 9 \\cdot 3 = 32$.

`); + + html += makeCard('rule', 'Характеристическое свойство', '15.2', ` +

В арифметической прогрессии любой средний член — это среднее арифметическое своих соседей:

+

$$a_n = \\dfrac{a_{n-1} + a_{n+1}}{2}\\quad (n \\ge 2).$$

+

Это свойство полностью характеризует арифметическую прогрессию: если оно выполняется для всех $n \\ge 2$, то $(a_n)$ — арифметическая прогрессия.

+

Пример. Между $4$ и $16$ в арифметической прогрессии стоит число $\\dfrac{4 + 16}{2} = 10$.

`); + + html += makeCard('example', 'Применение формулы', '15.3', ` +

а) Найти $a_{20}$, если $a_1 = 3$, $d = 4$:
+ $a_{20} = a_1 + 19 d = 3 + 19 \\cdot 4 = 3 + 76 = \\mathbf{79}.$

+

б) Найти $d$, если $a_1 = 2$, $a_{10} = 29$:
+ $29 = 2 + 9 d \\Rightarrow 9 d = 27 \\Rightarrow \\mathbf{d = 3}.$

+

в) Найти $a_1$, если $d = -2$, $a_5 = 10$:
+ $10 = a_1 + 4 \\cdot (-2) = a_1 - 8 \\Rightarrow \\mathbf{a_1 = 18}.$

`); + + /* INTERACTIVE 1 — конструктор арифм. прогрессии */ + html += `
+
ИНТЕРАКТИВ 1
Конструктор арифм. прогрессии
+
Двигай ползунки $a_1$ и $d$. Точки $(n; a_n)$ ложатся на прямую $y = a_1 + (x - 1) d$ — пунктирная линия.
+
+ + +
+
+
+ +
+
+
`; + + /* INTERACTIVE 2 — калькулятор a_n и d */ + html += `
+
ИНТЕРАКТИВ 2
Калькулятор $a_n$ и $d$
+
Две формы. Слева — найди $a_n$ по $a_1$, $d$, $n$. Справа — найди $d$ по $a_1$, $a_n$, $n$.
+
+
+
Найти $a_n$
+
+ $a_1$ = + $d$ = + $n$ = +
+ +
-
-

Содержание параграфа «Арифметическая прогрессия» будет добавлено в следующих обновлениях.

-

Раздел Phase 1.

+
+
Найти $d$
+
+ $a_1$ = + $a_n$ = + $n$ = +
+ +
-
` + secNav('p14', 'p16') + readButton('p15'); - renderMath(root); +
+
`; + + /* INTERACTIVE 3 — арифм. или нет */ + html += `
+
ИНТЕРАКТИВ 3
Является ли арифметической?
+
Даны первые члены последовательности. Если разность $a_{n+1} - a_n$ постоянна — это арифметическая прогрессия.
+
Задача: 1 / 6 · Очки: 0
+
+
+ + +
+ +
`; + + /* INTERACTIVE 4 — тренажёр */ + html += `
+
ИНТЕРАКТИВ 4
Тренажёр прогрессии
+
Используй формулы $a_n = a_1 + (n - 1) d$ и характеристическое свойство $a_n = \\dfrac{a_{n-1} + a_{n+1}}{2}$. Ответ — целое число.
+
Задача: 1 / 6 · Очки: 0
+
+
+ Ответ: + + +
+ +
`; + + box.innerHTML = html + secNav('p14', 'p16') + readButton('p15'); + renderMath(box); + + /* ===== IV1 wiring ===== */ + (function(){ + const svg = document.getElementById('p15-iv1-svg'); + const a1Sl = document.getElementById('p15-iv1-a1'); + const dSl = document.getElementById('p15-iv1-d'); + const av = document.getElementById('p15-iv1-av'); + const dv = document.getElementById('p15-iv1-dv'); + const formula = document.getElementById('p15-iv1-formula'); + const out = document.getElementById('p15-iv1-out'); + let bumped = false; + + function redraw(){ + const a1 = +a1Sl.value, d = +dSl.value; + av.textContent = a1; dv.textContent = d; + + // вычисляем 15 точек + const N = 15; + const pts = []; + for (let n=1;n<=N;n++) pts.push([n, a1 + (n-1)*d]); + const ys = pts.map(p=>p[1]); + let ymin = Math.min(...ys, 0), ymax = Math.max(...ys, 0); + const pad = Math.max(2, (ymax-ymin)*0.12); + ymin = Math.floor(ymin - pad); + ymax = Math.ceil(ymax + pad); + + const ax = axes2D(460, 300, 34, 0, N+1, ymin, ymax); + let g = ax.content; + + // пунктирная прямая y = a1 + (x-1) d + const xL = 0, xR = N + 1; + const yL = a1 + (xL - 1)*d; + const yR = a1 + (xR - 1)*d; + // зажмём в пределах ymin/ymax + function clip(x, y){ + if (y > ymax){ return [ x + (ymax - y) / (d || 1e-9) * 1, ymax ]; } + if (y < ymin){ return [ x + (ymin - y) / (d || 1e-9) * 1, ymin ]; } + return [x, y]; + } + const cL = clip(xL, yL), cR = clip(xR, yR); + g += ''; + + // точки + pts.forEach(p=>{ + if (p[1] < ymin || p[1] > ymax) return; + const x = ax.toX(p[0]); const y = ax.toY(p[1]); + g += ''; + }); + svg.innerHTML = g; + + // формула + const dPart = (d>=0 ? '+ ' + d : '- ' + (-d)); + formula.innerHTML = '$a_n = ' + a1 + ' ' + dPart + ' \\cdot (n - 1)$'; + + // первые 8 членов + const first8 = pts.slice(0,8).map(p=>p[1]).join(',\\ '); + out.innerHTML = 'Первые 8 членов: $' + first8 + ',\\ \\ldots$'; + renderMath(formula); renderMath(out); + if (!bumped){ bumped = true; bumpProgress('p15', 15); addXp(10,'p15-iv1'); } + } + a1Sl.addEventListener('input', redraw); + dSl.addEventListener('input', redraw); + redraw(); + })(); + + /* ===== IV2 wiring ===== */ + (function(){ + // Найти a_n + const outA = document.getElementById('p15-iv2a-out'); + let bumpedA = false; + document.getElementById('p15-iv2a-go').addEventListener('click', ()=>{ + const a1 = +document.getElementById('p15-iv2a-a1').value; + const d = +document.getElementById('p15-iv2a-d').value; + const n = +document.getElementById('p15-iv2a-n').value; + if (!Number.isInteger(n) || n < 1){ outA.innerHTML = 'Номер $n$ должен быть натуральным.'; return; } + const an = a1 + (n - 1) * d; + const dPart = (d>=0 ? '+ ' + d : '- ' + (-d)); + outA.innerHTML = '$a_{'+n+'} = '+a1+' '+dPart+' \\cdot ('+n+' - 1) = \\mathbf{'+fmt(an)+'}$'; + renderMath(outA); + if (!bumpedA){ bumpedA = true; bumpProgress('p15', 8); addXp(5,'p15-iv2a'); } + }); + // Найти d + const outB = document.getElementById('p15-iv2b-out'); + let bumpedB = false; + document.getElementById('p15-iv2b-go').addEventListener('click', ()=>{ + const a1 = +document.getElementById('p15-iv2b-a1').value; + const an = +document.getElementById('p15-iv2b-an').value; + const n = +document.getElementById('p15-iv2b-n').value; + if (!Number.isInteger(n) || n < 2){ outB.innerHTML = '$n$ должно быть целым и $\\ge 2$.'; renderMath(outB); return; } + const d = (an - a1) / (n - 1); + outB.innerHTML = '$d = \\dfrac{a_n - a_1}{n - 1} = \\dfrac{'+an+' - '+a1+'}{'+n+' - 1} = \\mathbf{'+fmt(d)+'}$'; + renderMath(outB); + if (!bumpedB){ bumpedB = true; bumpProgress('p15', 7); addXp(5,'p15-iv2b'); } + }); + })(); + + /* ===== IV3 wiring ===== */ + (function(){ + const items = [ + { q:'$1,\\ 4,\\ 7,\\ 10,\\ \\ldots$', ans:true, hint:'Разность $d = 3$ постоянна.' }, + { q:'$2,\\ 4,\\ 8,\\ 16,\\ \\ldots$', ans:false, hint:'Это геометрическая ($\\cdot 2$), не арифметическая.' }, + { q:'$5,\\ 5,\\ 5,\\ 5,\\ \\ldots$', ans:true, hint:'Это арифм. прогрессия с $d = 0$.' }, + { q:'$10,\\ 7,\\ 4,\\ 1,\\ \\ldots$', ans:true, hint:'$d = -3$ постоянна.' }, + { q:'$1,\\ 3,\\ 6,\\ 10,\\ \\ldots$', ans:false, hint:'Разности $2, 3, 4, \\ldots$ — не равны.' }, + { q:'$-2,\\ 1,\\ 4,\\ 7,\\ \\ldots$', ans:true, hint:'$d = 3$ постоянна.' } + ]; + let i = 0, sc = 0; + const idxEl = document.getElementById('p15-iv3-idx'); + const scEl = document.getElementById('p15-iv3-sc'); + const qEl = document.getElementById('p15-iv3-q'); + const fb = document.getElementById('p15-iv3-fb'); + const yBtn = document.getElementById('p15-iv3-y'); + const nBtn = document.getElementById('p15-iv3-n'); + let bumped = false; + function render(){ + idxEl.textContent = Math.min(i+1, items.length); + scEl.textContent = sc; + if (i >= items.length){ + qEl.innerHTML = 'Готово! Результат: ' + sc + ' / ' + items.length; + yBtn.disabled = true; nBtn.disabled = true; + yBtn.style.opacity = .5; nBtn.style.opacity = .5; + if (!bumped){ bumped = true; bumpProgress('p15', 25); addXp(15,'p15-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?'✓ Верно. ':'✗ Неверно. ') + it.hint); + i++; + setTimeout(render, 1000); + } + yBtn.addEventListener('click', ()=>answer(true)); + nBtn.addEventListener('click', ()=>answer(false)); + render(); + })(); + + /* ===== IV4 wiring — тренажёр ===== */ + (function(){ + const items = [ + { q:'$a_1 = 2,\\ d = 3$. Найти $a_5$.', ans: 14 }, + { q:'$a_1 = 10,\\ d = -2$. Найти $a_{10}$.', ans: -8 }, + { q:'$a_1 = 5,\\ a_4 = 11$. Найти $d$.', ans: 2 }, + { q:'$a_3 = 7,\\ d = 3$. Найти $a_1$.', ans: 1 }, + { q:'$a_n = 3 + 2(n - 1)$. Найти $a_{50}$.', ans: 101 }, + { q:'Найти число, стоящее между $4$ и $16$ в арифм. прогрессии (т.е. среднее арифметическое).', ans: 10 } + ]; + let i = 0, sc = 0; + const idxEl = document.getElementById('p15-iv4-idx'); + const scEl = document.getElementById('p15-iv4-sc'); + const qEl = document.getElementById('p15-iv4-q'); + const inp = document.getElementById('p15-iv4-ans'); + const btn = document.getElementById('p15-iv4-go'); + const fb = document.getElementById('p15-iv4-fb'); + let bumped = false; + function render(){ + idxEl.textContent = Math.min(i+1, items.length); + scEl.textContent = sc; + if (i >= items.length){ + qEl.innerHTML = 'Готово! Результат: ' + sc + ' / ' + items.length; + inp.disabled = true; btn.disabled = true; + inp.style.opacity = .5; btn.style.opacity = .5; + if (!bumped){ bumped = true; bumpProgress('p15', 25); addXp(15,'p15-iv4'); achievement('p15_done'); } + 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 ? '✓ Верно: $'+it.ans+'$' : '✗ Неверно. Правильный ответ: $'+it.ans+'$'); + i++; + setTimeout(render, 1000); + }); + inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } }); + render(); + })(); + wireReadBtn('p15'); }