feat(alg11 ch3 wave1): §7 «Свойства логарифмов»

This commit is contained in:
Maxim Dolgolyov
2026-05-29 12:10:52 +03:00
parent c931eeacd6
commit aee927a3b1
+444 -4
View File
@@ -130,6 +130,26 @@ a{color:inherit;text-decoration:none}
.spoiler[open] summary::before{content:'\2212'}
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
.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:.82rem;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)}
@@ -450,6 +470,19 @@ function makeCard(kind, title, num, body){
const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно'};
return '<div class="card"><div class="card-header"><div class="card-icon '+kind+'">'+ICONS[kind]+'</div><div class="card-title">'+(labels[kind]||'')+(title&&title!==labels[kind]?' \xb7 '+title:'')+'</div>'+(num?'<div class="card-num">'+num+'</div>':'')+'</div><div class="card-body">'+body+'</div></div>';
}
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='<span class="dnd-txt">'+it.html+'</span><span class="dnd-x" title="Убрать">\xd7</span>'; 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-хелперы (axes2D, plotFunc, pointWithDrop, asymptote, snapToValue, геом.) === */
@@ -604,15 +637,422 @@ function wireReadBtn(paraId){
function buildP7(){
const box = document.getElementById('p7-body');
let html = '';
html += makeCard('theory', 'В разработке', '7.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">Ключевая формула: $\log_a(bc) = \log_a b + \log_a c$</p>
/* === ТЕОРИЯ === */
html += makeCard('theory', 'Произведение, частное, степень', '7.1', `
<p>Пусть $a > 0$, $a \\ne 1$ и $b > 0$, $c > 0$. Тогда справедливы три основных свойства:</p>
<p style="text-align:center;font-size:1.04rem">$\\log_a(bc) = \\log_a b + \\log_a c$</p>
<p style="text-align:center;font-size:1.04rem">$\\log_a \\dfrac{b}{c} = \\log_a b - \\log_a c$</p>
<p style="text-align:center;font-size:1.04rem">$\\log_a b^n = n \\cdot \\log_a b$</p>
<details class="spoiler"><summary>Доказательство свойства произведения</summary><div class="spoiler-body">
<p>Пусть $\\log_a b = p$ и $\\log_a c = q$. По определению логарифма $b = a^p$ и $c = a^q$.</p>
<p>Тогда $bc = a^p \\cdot a^q = a^{p+q}$, значит $\\log_a(bc) = p + q = \\log_a b + \\log_a c$.</p>
<p>Свойства для частного и степени доказываются аналогично — через определение логарифма и свойства степеней.</p>
</div></details>
<p style="margin-top:10px"><b>Примеры:</b></p>
<p>$\\log_2 6 = \\log_2(2 \\cdot 3) = \\log_2 2 + \\log_2 3 = 1 + \\log_2 3$.</p>
<p>$\\log_5 \\dfrac{25}{8} = \\log_5 25 - \\log_5 8 = 2 - 3 \\log_5 2$.</p>
<p>$\\log_2 8^3 = 3 \\log_2 8 = 3 \\cdot 3 = 9$.</p>
`);
html += makeCard('rule', 'Корень и перенос показателя из основания', '7.2', `
<p><b>Логарифм корня.</b> Поскольку $\\sqrt[n]{b} = b^{1/n}$, по свойству степени:</p>
<p style="text-align:center;font-size:1.04rem">$\\log_a \\sqrt[n]{b} = \\log_a b^{1/n} = \\dfrac{1}{n} \\log_a b$</p>
<p><b>Перенос показателя из основания.</b></p>
<p style="text-align:center;font-size:1.04rem">$\\log_{a^n} b = \\dfrac{1}{n} \\log_a b$</p>
<p>Эти два свойства часто объединяют в одно:</p>
<p style="text-align:center;font-size:1.06rem">$\\log_{a^k} b^m = \\dfrac{m}{k} \\log_a b$</p>
<p style="margin-top:10px"><b>Примеры:</b></p>
<p>$\\log_2 \\sqrt{8} = \\dfrac{1}{2} \\log_2 8 = \\dfrac{3}{2}$.</p>
<p>$\\log_4 16 = \\log_{2^2} 2^4 = \\dfrac{4}{2} \\log_2 2 = 2$.</p>
<p>$\\log_{27} \\sqrt[3]{9} = \\log_{3^3} 3^{2/3} = \\dfrac{2/3}{3} = \\dfrac{2}{9}$.</p>
`);
html += makeCard('example', 'Переход к новому основанию', '7.3', `
<p>Иногда удобно перейти от $\\log_a b$ к логарифму по другому основанию $c$ ($c > 0$, $c \\ne 1$):</p>
<p style="text-align:center;font-size:1.06rem">$\\log_a b = \\dfrac{\\log_c b}{\\log_c a}$</p>
<p>Чаще всего берут $c = 10$ (десятичные) или $c = e$ (натуральные). Полезное следствие при $a \\ne 1$, $b \\ne 1$:</p>
<p style="text-align:center;font-size:1.04rem">$\\log_a b = \\dfrac{1}{\\log_b a}$</p>
<p style="margin-top:10px"><b>Применение:</b></p>
<p>$\\log_2 7 = \\dfrac{\\lg 7}{\\lg 2} \\approx \\dfrac{0{,}845}{0{,}301} \\approx 2{,}807$.</p>
<p>$\\log_3 5 = \\dfrac{1}{\\log_5 3}$.</p>
<p><b>Связь $\\lg$ и $\\ln$:</b></p>
<p>$\\lg b = \\dfrac{\\ln b}{\\ln 10} \\approx \\dfrac{\\ln b}{2{,}303}$, $\\quad \\ln b = \\dfrac{\\lg b}{\\lg e} \\approx \\dfrac{\\lg b}{0{,}4343}$.</p>
`);
/* === ИНТЕРАКТИВ 1 — пошаговый «применитель» свойств === */
html += `<div class="wg" id="p7-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Применение свойств логарифма</div></div>
<div class="wg-help">Выбери выражение ползунком и нажимай «Следующий шаг ▶», чтобы открывать решение по одному шагу. Просмотри все 5 выражений — получишь XP.</div>
<div class="sliders">
<label>Выражение № <b id="p7-iv1-n">1</b> / 5<input type="range" id="p7-iv1-sn" min="1" max="5" step="1" value="1"></label>
</div>
<div id="p7-iv1-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.10rem;text-align:center;margin-bottom:10px"></div>
<div id="p7-iv1-steps" style="display:flex;flex-direction:column;gap:8px;margin-bottom:10px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p7-iv1-next">Следующий шаг ▶</button>
<button class="btn" id="p7-iv1-all">Показать все шаги</button>
<button class="btn" id="p7-iv1-reset">Скрыть шаги</button>
</div>
</div>`;
/* === ИНТЕРАКТИВ 2 — калькулятор логарифмов === */
html += `<div class="wg" id="p7-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор логарифмов</div></div>
<div class="wg-help">Выбери свойство, заполни поля и нажми «Вычислить» — увидишь применение свойства пошагово и численное приближение.</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:center;margin-bottom:12px">
<button class="btn" data-mode="prod" id="p7-iv2-m-prod">Произведение $\\log_a(bc)$</button>
<button class="btn" data-mode="quot" id="p7-iv2-m-quot">Частное $\\log_a(b/c)$</button>
<button class="btn" data-mode="pow" id="p7-iv2-m-pow">Степень $\\log_a b^n$</button>
<button class="btn" data-mode="base" id="p7-iv2-m-base">Переход к новому основанию</button>
</div>
<div id="p7-iv2-inputs" style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:10px"></div>
<div style="text-align:center;margin-bottom:10px"><button class="btn primary" id="p7-iv2-go">Вычислить</button></div>
<div id="p7-iv2-out" style="padding:12px 14px;background:var(--card);border-radius:9px;font-size:.96rem;min-height:60px;line-height:1.8"></div>
<div class="feedback" id="p7-iv2-fb"></div>
</div>`;
/* === ИНТЕРАКТИВ 3 — DnD-сортер === */
html += `<div class="wg" id="p7-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Сопоставь выражение со значением</div></div>
<div class="wg-help">Перетащи каждое выражение в ящик с верным значением. Используй свойства логарифмов: сумму, разность, степень и корень.</div>
<div class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11V6a3 3 0 0 1 6 0v5"/><path d="M9 11h6v8a4 4 0 0 1-8 0z"/></svg> 8 выражений — 4 ящика</div>
<div id="p7-iv3-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="v1">$=1$</h5><div class="drop-items" data-cat="v1"></div></div>
<div class="drop-box"><h5 data-cat="v2">$=2$</h5><div class="drop-items" data-cat="v2"></div></div>
<div class="drop-box"><h5 data-cat="v3">$=3$</h5><div class="drop-items" data-cat="v3"></div></div>
<div class="drop-box"><h5 data-cat="v5">$=5$</h5><div class="drop-items" data-cat="v5"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p7-iv3-check">Проверить</button><button class="btn" id="p7-iv3-reset">Сначала</button></div>
<div class="feedback" id="p7-iv3-fb"></div>
</div>`;
/* === ИНТЕРАКТИВ 4 — тренажёр === */
html += `<div class="wg" id="p7-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр свойств</div></div>
<div class="wg-help">6 задач на применение свойств. Введи число (целое или десятичное, допуск $\\pm 0{,}05$).</div>
<div class="score-display"><span>Задача <b id="p7-iv4-i">1</b> / 6</span><span>Очки: <b id="p7-iv4-s">0</b> / 6</span></div>
<div id="p7-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.12rem;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="p7-iv4-ans" class="tinp" style="width:120px;text-align:center" step="0.01">
<button class="btn primary" id="p7-iv4-go">Проверить</button>
<button class="btn" id="p7-iv4-start">Заново</button>
</div>
<div class="feedback" id="p7-iv4-fb"></div>
</div>`;
html += secNavFor('p7');
html += readButton('p7');
box.innerHTML = html;
renderMath(box);
/* === IV1 — пошаговый «применитель» === */
(function(){
const TASKS = [
{
q: '$\\log_2 24 - \\log_2 3$',
steps: [
'Используем свойство <b>логарифма частного</b>: $\\log_a b - \\log_a c = \\log_a \\dfrac{b}{c}$.',
'Получаем $\\log_2 24 - \\log_2 3 = \\log_2 \\dfrac{24}{3} = \\log_2 8$.',
'Так как $8 = 2^3$, имеем $\\log_2 8 = 3$.',
'<b>Ответ:</b> $3$.'
]
},
{
q: '$\\log_3 5 + \\log_3 18$',
steps: [
'Используем свойство <b>логарифма произведения</b>: $\\log_a b + \\log_a c = \\log_a(bc)$.',
'Получаем $\\log_3 5 + \\log_3 18 = \\log_3(5 \\cdot 18) = \\log_3 90$.',
'Заметим, что $90 = 9 \\cdot 10$, поэтому $\\log_3 90 = \\log_3 9 + \\log_3 10 = 2 + \\log_3 10$.',
'<b>Ответ:</b> $2 + \\log_3 10 \\approx 4{,}096$.'
]
},
{
q: '$2 \\log_5 3 + \\log_5 \\dfrac{25}{9}$',
steps: [
'По свойству степени: $2 \\log_5 3 = \\log_5 3^2 = \\log_5 9$.',
'Получаем $\\log_5 9 + \\log_5 \\dfrac{25}{9} = \\log_5\\left(9 \\cdot \\dfrac{25}{9}\\right) = \\log_5 25$.',
'Так как $25 = 5^2$, имеем $\\log_5 25 = 2$.',
'<b>Ответ:</b> $2$.'
]
},
{
q: '$\\log_2 \\sqrt[3]{32}$',
steps: [
'Применяем свойство <b>логарифма корня</b>: $\\log_a \\sqrt[n]{b} = \\dfrac{1}{n} \\log_a b$.',
'Получаем $\\log_2 \\sqrt[3]{32} = \\dfrac{1}{3} \\log_2 32$.',
'Так как $32 = 2^5$, имеем $\\log_2 32 = 5$, значит $\\dfrac{1}{3} \\cdot 5 = \\dfrac{5}{3}$.',
'<b>Ответ:</b> $\\dfrac{5}{3} \\approx 1{,}667$.'
]
},
{
q: '$\\log_{16} 8$',
steps: [
'Перепишем основание и аргумент по основанию $2$: $16 = 2^4$, $8 = 2^3$.',
'Применяем формулу $\\log_{a^k} b^m = \\dfrac{m}{k} \\log_a b$: $\\log_{2^4} 2^3 = \\dfrac{3}{4} \\log_2 2 = \\dfrac{3}{4}$.',
'<b>Ответ:</b> $\\dfrac{3}{4} = 0{,}75$.'
]
}
];
const sn = document.getElementById('p7-iv1-sn');
const nL = document.getElementById('p7-iv1-n');
const qEl = document.getElementById('p7-iv1-q');
const stepsEl = document.getElementById('p7-iv1-steps');
const nextBtn = document.getElementById('p7-iv1-next');
const allBtn = document.getElementById('p7-iv1-all');
const resetBtn = document.getElementById('p7-iv1-reset');
const seen = new Set();
let _done = false;
let cur = 0, shown = 0;
function render(){
const t = TASKS[cur];
nL.textContent = (cur + 1);
qEl.innerHTML = t.q;
stepsEl.innerHTML = t.steps.map((s, i) => {
const visible = i < shown;
const isLast = i === t.steps.length - 1 && visible;
const bg = isLast ? '#dcfce7' : 'var(--card)';
const brd = isLast ? '2px solid #16a34a' : '1px solid var(--border)';
return '<div data-i="'+i+'" style="padding:10px 12px;background:'+bg+';border:'+brd+';border-radius:8px;font-size:.95rem;display:'+(visible?'block':'none')+'"><span style="color:var(--muted);font-weight:700;margin-right:6px">Шаг '+(i+1)+':</span>'+s+'</div>';
}).join('');
renderMath(qEl);
renderMath(stepsEl);
if(shown >= t.steps.length){
seen.add(cur);
if(!_done && seen.size >= 5){ _done = true; addXp(10, 'p7-iv1'); bumpProgress('p7', 15); }
}
}
function load(n){ cur = Math.max(0, Math.min(TASKS.length - 1, n)); shown = 1; render(); }
sn.addEventListener('input', () => load((+sn.value) - 1));
nextBtn.addEventListener('click', () => {
if(shown < TASKS[cur].steps.length){ shown++; render(); }
});
allBtn.addEventListener('click', () => { shown = TASKS[cur].steps.length; render(); });
resetBtn.addEventListener('click', () => { shown = 1; render(); });
load(0);
})();
/* === IV2 — калькулятор === */
(function(){
const modes = ['prod','quot','pow','base'];
const inputsEl = document.getElementById('p7-iv2-inputs');
const outEl = document.getElementById('p7-iv2-out');
const fbEl = document.getElementById('p7-iv2-fb');
const goBtn = document.getElementById('p7-iv2-go');
let mode = 'prod';
let used = new Set();
let _done = false;
function setMode(m){
mode = m;
modes.forEach(mm => {
const btn = document.getElementById('p7-iv2-m-'+mm);
if(btn) btn.classList.toggle('primary', mm === m);
});
fbEl.style.display = 'none';
outEl.innerHTML = '<span style="color:var(--muted)">Нажми «Вычислить», чтобы увидеть шаги.</span>';
if(m === 'prod'){
inputsEl.innerHTML =
'<span style="font-family:JetBrains Mono,monospace">$a$ =</span>'+
'<input type="number" id="p7-iv2-a" class="tinp" style="width:70px;text-align:center" value="2" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$b$ =</span>'+
'<input type="number" id="p7-iv2-b" class="tinp" style="width:70px;text-align:center" value="4" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$c$ =</span>'+
'<input type="number" id="p7-iv2-c" class="tinp" style="width:70px;text-align:center" value="8" step="1" min="0.1">';
} else if(m === 'quot'){
inputsEl.innerHTML =
'<span style="font-family:JetBrains Mono,monospace">$a$ =</span>'+
'<input type="number" id="p7-iv2-a" class="tinp" style="width:70px;text-align:center" value="2" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$b$ =</span>'+
'<input type="number" id="p7-iv2-b" class="tinp" style="width:70px;text-align:center" value="32" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$c$ =</span>'+
'<input type="number" id="p7-iv2-c" class="tinp" style="width:70px;text-align:center" value="8" step="1" min="0.1">';
} else if(m === 'pow'){
inputsEl.innerHTML =
'<span style="font-family:JetBrains Mono,monospace">$a$ =</span>'+
'<input type="number" id="p7-iv2-a" class="tinp" style="width:70px;text-align:center" value="2" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$b$ =</span>'+
'<input type="number" id="p7-iv2-b" class="tinp" style="width:70px;text-align:center" value="8" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$n$ =</span>'+
'<input type="number" id="p7-iv2-n" class="tinp" style="width:70px;text-align:center" value="3" step="1">';
} else { // base
inputsEl.innerHTML =
'<span style="font-family:JetBrains Mono,monospace">$a$ =</span>'+
'<input type="number" id="p7-iv2-a" class="tinp" style="width:70px;text-align:center" value="2" step="0.5" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$b$ =</span>'+
'<input type="number" id="p7-iv2-b" class="tinp" style="width:70px;text-align:center" value="7" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$c$ =</span>'+
'<input type="number" id="p7-iv2-c" class="tinp" style="width:70px;text-align:center" value="10" step="1" min="0.1">';
}
renderMath(inputsEl);
}
modes.forEach(m => {
const btn = document.getElementById('p7-iv2-m-'+m);
if(btn) btn.addEventListener('click', () => setMode(m));
});
setMode('prod');
function num(id){ const el = document.getElementById(id); return el ? +el.value : NaN; }
function r(n){ return (Math.abs(n - Math.round(n)) < 1e-9) ? Math.round(n) : (+n.toFixed(4)); }
goBtn.addEventListener('click', () => {
fbEl.style.display = 'none';
const a = num('p7-iv2-a'), b = num('p7-iv2-b'), c = num('p7-iv2-c'), n = num('p7-iv2-n');
// Базовая валидация
if(mode !== 'pow'){
if(!isFinite(a) || a <= 0 || a === 1 || !isFinite(b) || b <= 0 || !isFinite(c) || c <= 0){
feedback(fbEl, false, '&#10007; Требуется $a > 0$, $a \\ne 1$, $b > 0$, $c > 0$.'); return;
}
} else {
if(!isFinite(a) || a <= 0 || a === 1 || !isFinite(b) || b <= 0 || !isFinite(n)){
feedback(fbEl, false, '&#10007; Требуется $a > 0$, $a \\ne 1$, $b > 0$.'); return;
}
}
let out = '';
if(mode === 'prod'){
const sum = Math.log(b*c) / Math.log(a);
const lb = Math.log(b) / Math.log(a);
const lc = Math.log(c) / Math.log(a);
out =
'<div>$\\log_{'+r(a)+'}('+r(b)+' \\cdot '+r(c)+') = \\log_{'+r(a)+'} '+r(b)+' + \\log_{'+r(a)+'} '+r(c)+'$</div>'+
'<div>$= '+r(lb)+' + '+r(lc)+' \\approx '+r(sum)+'$</div>'+
'<div style="color:var(--muted);font-size:.86rem;margin-top:6px">Проверка через Math.log: $\\log_{'+r(a)+'}('+r(b*c)+') \\approx '+r(sum)+'$.</div>';
} else if(mode === 'quot'){
const diff = Math.log(b/c) / Math.log(a);
const lb = Math.log(b) / Math.log(a);
const lc = Math.log(c) / Math.log(a);
out =
'<div>$\\log_{'+r(a)+'} \\dfrac{'+r(b)+'}{'+r(c)+'} = \\log_{'+r(a)+'} '+r(b)+' - \\log_{'+r(a)+'} '+r(c)+'$</div>'+
'<div>$= '+r(lb)+' - '+r(lc)+' \\approx '+r(diff)+'$</div>'+
'<div style="color:var(--muted);font-size:.86rem;margin-top:6px">Проверка: $\\log_{'+r(a)+'}('+r(b/c)+') \\approx '+r(diff)+'$.</div>';
} else if(mode === 'pow'){
const lb = Math.log(b) / Math.log(a);
const val = n * lb;
out =
'<div>$\\log_{'+r(a)+'} '+r(b)+'^{'+r(n)+'} = '+r(n)+' \\cdot \\log_{'+r(a)+'} '+r(b)+'$</div>'+
'<div>$= '+r(n)+' \\cdot '+r(lb)+' \\approx '+r(val)+'$</div>'+
'<div style="color:var(--muted);font-size:.86rem;margin-top:6px">Проверка: $\\log_{'+r(a)+'}('+r(Math.pow(b,n))+') \\approx '+r(val)+'$.</div>';
} else { // base
if(c === 1){ feedback(fbEl, false, '&#10007; Новое основание $c$ не должно быть равно $1$.'); return; }
const lab = Math.log(b) / Math.log(a);
const lcb = Math.log(b) / Math.log(c);
const lca = Math.log(a) / Math.log(c);
out =
'<div>$\\log_{'+r(a)+'} '+r(b)+' = \\dfrac{\\log_{'+r(c)+'} '+r(b)+'}{\\log_{'+r(c)+'} '+r(a)+'}$</div>'+
'<div>$\\approx \\dfrac{'+r(lcb)+'}{'+r(lca)+'} \\approx '+r(lab)+'$</div>'+
'<div style="color:var(--muted);font-size:.86rem;margin-top:6px">Прямое значение: $\\log_{'+r(a)+'} '+r(b)+' \\approx '+r(lab)+'$.</div>';
}
outEl.innerHTML = out;
renderMath(outEl);
used.add(mode);
if(!_done && used.size >= 3){ _done = true; addXp(10, 'p7-iv2'); bumpProgress('p7', 15); }
});
})();
/* === IV3 — DnD-сортер === */
(function(){
const items = [
{id:'s1', html:'$\\log_2 8 + \\log_2 4$', cat:'v5'},
{id:'s2', html:'$\\log_3 81 - \\log_3 3$', cat:'v3'},
{id:'s3', html:'$2 \\log_5 5$', cat:'v2'},
{id:'s4', html:'$\\log_2 32 - \\log_2 8$', cat:'v2'},
{id:'s5', html:'$\\log_3 9 + \\log_3 3$', cat:'v3'},
{id:'s6', html:'$\\log_2 \\sqrt[3]{8}$', cat:'v1'},
{id:'s7', html:'$\\log_5 125 + \\log_5 1$', cat:'v3'},
{id:'s8', html:'$\\log_4 16 + \\log_4 4$', cat:'v3'}
];
const cats = ['v1','v2','v3','v5'];
const sorter = setupSorter({
poolId: 'p7-iv3-pool',
scopeSelector: '#p7-iv3',
items, cats
});
const checkBtn = document.getElementById('p7-iv3-check');
const resetBtn = document.getElementById('p7-iv3-reset');
const fbEl = document.getElementById('p7-iv3-fb');
let _done = false;
checkBtn.addEventListener('click', () => {
let ok = 0, total = items.length;
let missed = [];
items.forEach(it => {
if(sorter.placed[it.id] === it.cat) ok++;
else if(!sorter.placed[it.id]) missed.push(it.id);
});
if(ok === total){
feedback(fbEl, true, '&#10003; Отлично! Все 8 выражений на месте.');
if(!_done){ _done = true; addXp(15, 'p7-iv3'); bumpProgress('p7', 25); }
} else {
const left = missed.length;
feedback(fbEl, false, '&#10007; Верно: '+ok+' / '+total+'. '+(left ? 'Не размещено: '+left+'.' : 'Проверь свойства логарифмов.'));
}
});
resetBtn.addEventListener('click', () => {
sorter.reset();
fbEl.style.display = 'none';
});
})();
/* === IV4 — тренажёр === */
(function(){
const TASKS = [
{ q: '$\\log_2 6 + \\log_2 \\dfrac{16}{3}$', a: 5, hint: '$\\log_2(6 \\cdot 16/3) = \\log_2 32 = 5$.' },
{ q: '$\\log_3 \\sqrt{27}$', a: 1.5, hint: '$\\dfrac{1}{2} \\log_3 27 = \\dfrac{3}{2}$.' },
{ q: '$\\log_5 100 - \\log_5 4$', a: 2, hint: '$\\log_5 (100/4) = \\log_5 25 = 2$.' },
{ q: '$\\log_4 64$', a: 3, hint: '$\\log_{2^2} 2^6 = 6/2 = 3$.' },
{ q: '$\\log_2 3 \\cdot \\log_3 8$', a: 3, hint: '$\\log_2 3 \\cdot \\dfrac{\\log_2 8}{\\log_2 3} = \\log_2 8 = 3$.' },
{ q: '$\\dfrac{\\lg 100}{\\lg 10}$', a: 2, hint: '$\\lg 100 / \\lg 10 = 2/1 = 2$.' }
];
const qEl = document.getElementById('p7-iv4-q');
const ansEl = document.getElementById('p7-iv4-ans');
const goBtn = document.getElementById('p7-iv4-go');
const startBtn = document.getElementById('p7-iv4-start');
const iEl = document.getElementById('p7-iv4-i');
const sEl = document.getElementById('p7-iv4-s');
const fbEl = document.getElementById('p7-iv4-fb');
let idx = 0, score = 0;
let _done = false;
function render(){
iEl.textContent = (idx + 1);
sEl.textContent = score;
if(idx >= TASKS.length){
qEl.innerHTML = '<b style="color:var(--ok)">Все задачи решены! Очков: '+score+' / '+TASKS.length+'.</b>';
ansEl.disabled = true; goBtn.disabled = true;
if(!_done){ _done = true; addXp(15, 'p7-iv4'); bumpProgress('p7', 25); }
renderMath(qEl);
return;
}
qEl.innerHTML = TASKS[idx].q;
ansEl.value = '';
ansEl.disabled = false; goBtn.disabled = false;
fbEl.style.display = 'none';
renderMath(qEl);
setTimeout(() => ansEl.focus(), 30);
}
goBtn.addEventListener('click', () => {
if(idx >= TASKS.length) return;
const v = +ansEl.value;
if(!isFinite(v) || ansEl.value === ''){ feedback(fbEl, false, '&#10007; Введи число.'); return; }
const t = TASKS[idx];
if(Math.abs(v - t.a) < 0.05){
score++; sEl.textContent = score;
feedback(fbEl, true, '&#10003; Верно! '+t.hint);
setTimeout(() => { idx++; render(); }, 950);
} else {
feedback(fbEl, false, '&#10007; Неверно. Подсказка: '+t.hint+' Правильный ответ: <b>'+t.a+'</b>.');
setTimeout(() => { idx++; render(); }, 1900);
}
});
ansEl.addEventListener('keydown', e => { if(e.key === 'Enter'){ e.preventDefault(); goBtn.click(); } });
startBtn.addEventListener('click', () => { idx = 0; score = 0; _done = false; render(); });
render();
})();
wireReadBtn('p7');
}