feat(math5): Глава 1 §13–§14 — признаки делимости, простые/составные числа

§13 Признаки делимости (на 2/3/4/5/9/10; живой чекер: вводишь число → флажки
с объяснениями + квиз «делится ли нацело»). §14 Простые/составные (определения,
разложение на множители; интерактивное решето Эратосфена «найди простые 2..30»
+ квиз «простое или составное»). Шпаргалки/типсы §13–14. Тесты math5: 8/8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-03 09:38:10 +03:00
parent a4a0ae1a77
commit 6e64339e8a
2 changed files with 154 additions and 1 deletions
+4
View File
@@ -112,6 +112,10 @@ test('ch1: §1 «как решать задачу», §2 «разрядная т
assert.ok(doc.querySelector('#p11-fig svg circle'), '§11: точки-группы с остатком');
win.goTo('p12'); await wait(80);
assert.ok(doc.querySelector('#p12-fig'), '§12: делители-чипсы (НОД)');
win.goTo('p13'); await wait(80);
assert.ok(doc.querySelector('#p13-out'), '§13: чекер делимости');
win.goTo('p14'); await wait(80);
assert.equal(doc.querySelectorAll('#p14-grid [data-n]').length, 29, '§14: решето 2..30');
win.goTo('final'); await wait(80);
assert.ok(doc.querySelector('#fin-go'), 'финал: арена боссов');
win.bumpProgress('final', 100); await wait(20);
+150 -1
View File
@@ -1049,6 +1049,145 @@ function buildP12(){
})();
}
/* ===================== § 13. ПРИЗНАКИ ДЕЛИМОСТИ ===================== */
function buildP13(){
var box=document.getElementById('p13-body'); var h='';
h+=makeCard('oral','Где это в жизни','13.0',
'<p>Иногда нужно быстро понять: разделится ли число нацело — <b>не выполняя деления</b>. Можно ли $87$ конфет раздать поровну троим? А $90$? Признаки делимости дают ответ за секунду.</p>');
h+=makeCard('rule','Признаки делимости','13.1',
'<p><b>На 2</b> — если последняя цифра чётная ($0,2,4,6,8$).</p>'
+'<p><b>На 5</b> — если последняя цифра $0$ или $5$.</p>'
+'<p><b>На 10</b> — если последняя цифра $0$.</p>'
+'<p><b>На 3</b> — если сумма цифр делится на $3$.</p>'
+'<p><b>На 9</b> — если сумма цифр делится на $9$.</p>'
+'<p><b>На 4</b> — если число из двух последних цифр делится на $4$.</p>');
h+=makeCard('example','Примеры','13.2',
'<p>$540$ делится на $2,5,10$ (оканчивается на $0$) и на $3$ (сумма цифр $5+4+0=9$).</p>'
+'<p>$123$ делится на $3$ ($1+2+3=6$), но не на $2$ и не на $9$.</p>');
h+=makeCard('example','Разбор по шагам','13.3',
'<p>Проверим, делится ли $5\\,814$ на $2$, $3$ и $9$.</p>'
+'<ol style="padding-left:22px;line-height:2">'
+'<li>На $2$: последняя цифра $4$ — чётная. <b>Делится.</b></li>'
+'<li>Сумма цифр: $5+8+1+4=18$.</li>'
+'<li>На $3$: $18$ делится на $3$. <b>Делится.</b></li>'
+'<li>На $9$: $18$ делится на $9$. <b>Делится.</b></li>'
+'</ol>');
h+=makeCard('theory','А знаешь ли ты?','13.4',
'<p>Есть признак и для $11$: складывают цифры через одну и вычитают суммы. Если разность делится на $11$ — и число делится. Например, $2\\,728$: $(8+7)-(2+2)=11$ — делится на $11$!</p>');
h+='<div class="wg" id="p13-iv1"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><div class="wg-title">Проверка делимости</div></div>'
+'<div class="wg-help">Введи число — увидишь, на что оно делится, и почему.</div>'
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap;margin-bottom:8px"><input type="number" id="p13-in" class="tinp" style="width:150px;text-align:center" value="540"><button class="btn primary" id="p13-chk">Проверить</button></div>'
+'<div id="p13-out"></div></div>';
h+='<div class="wg" id="p13-iv2"><div class="wg-header"><span class="wg-badge">Интерактив 2</span><div class="wg-title">Делится ли нацело?</div></div>'
+'<div class="wg-help">Используй признак делимости и ответь «да» или «нет».</div>'
+'<div class="score-display"><span>Вопрос <b id="p13-i">1</b> / 6</span><span>Очки: <b id="p13-s">0</b> / 6</span></div>'
+'<div id="p13-q" class="qbox"></div>'
+'<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap"><button class="btn primary" data-yn="1">Да</button><button class="btn primary" data-yn="0">Нет</button></div>'
+'<div class="feedback" id="p13-fb"></div></div>';
h+=secNav('p12','p14')+readBtn('p13');
box.innerHTML=h; renderMath(box);
(function(){
function check(){
var raw=(document.getElementById('p13-in').value||'').replace(/\D/g,''); if(!raw)raw='0'; var n=parseInt(raw,10);
var ds=raw.split('').reduce(function(a,c){return a+(+c);},0);
var rules=[
['на 2', n%2===0, 'последняя цифра '+raw[raw.length-1]],
['на 3', n%3===0, 'сумма цифр '+ds],
['на 4', n%4===0, 'две последние: '+raw.slice(-2)],
['на 5', n%5===0, 'последняя цифра '+raw[raw.length-1]],
['на 9', n%9===0, 'сумма цифр '+ds],
['на 10', n%10===0, 'последняя цифра '+raw[raw.length-1]]
];
var s='<div style="display:flex;flex-wrap:wrap;gap:8px;justify-content:center">';
rules.forEach(function(r){ s+='<div style="padding:8px 12px;border-radius:9px;font-weight:700;min-width:120px;text-align:center;'+(r[1]?'background:#d1fae5;color:#065f46':'background:#fee2e2;color:#7f1d1d')+'">'+(r[1]?'✓ ':'✗ ')+r[0]+'<div style="font-size:.72rem;font-weight:500;opacity:.85;margin-top:2px">'+r[2]+'</div></div>'; });
document.getElementById('p13-out').innerHTML=s+'</div>';
}
document.getElementById('p13-chk').addEventListener('click',check);
document.getElementById('p13-in').addEventListener('keydown',function(e){ if(e.key==='Enter')check(); });
check();
})();
(function(){
var i=0,score=0,cur=null;
function gen(){ var d=_pick([2,3,5,9,4,10]), n=_ri(100,999); cur={n:n,d:d,yes:n%d===0}; }
function show(){ if(i>=6){ document.getElementById('p13-q').innerHTML='<b>Готово! '+score+' / 6</b>'; if(score>=5){addXp(15,'p13-iv2');bumpProgress('p13',30);}else if(score>=3){addXp(8,'p13-iv2');bumpProgress('p13',16);} return; }
gen(); document.getElementById('p13-i').textContent=i+1;
document.getElementById('p13-q').innerHTML='Делится ли $'+cur.n+'$ нацело на $'+cur.d+'$?'; renderMath(document.getElementById('p13-q'));
document.getElementById('p13-fb').style.display='none'; }
function ans(yn){ if(i>=6)return; var fb=document.getElementById('p13-fb');
if((yn===1)===cur.yes){ score++; feedback(fb,true,'✓ Верно! '+cur.n+(cur.yes?' делится':' не делится')+' на '+cur.d+'.'); } else feedback(fb,false,'✗ Нет. '+cur.n+(cur.yes?' делится':' не делится')+' на '+cur.d+'.');
document.getElementById('p13-s').textContent=score; i++; setTimeout(show,1200); }
document.querySelectorAll('#p13-iv2 [data-yn]').forEach(function(b){ b.addEventListener('click',function(){ ans(+b.getAttribute('data-yn')); }); });
show();
})();
}
/* ===================== § 14. ПРОСТЫЕ И СОСТАВНЫЕ ЧИСЛА ===================== */
function buildP14(){
var box=document.getElementById('p14-body'); var h='';
h+=makeCard('oral','Где это в жизни','14.0',
'<p>Простые числа — «кирпичики», из которых умножением складываются все остальные числа. На свойствах простых чисел держится защита банковских карт и переписки в интернете: огромное число легко перемножить, но почти невозможно разложить обратно.</p>');
h+=makeCard('theory','Простые и составные','14.1',
'<p><b>Простое число</b> имеет ровно <b>два</b> делителя: $1$ и само себя ($2,3,5,7,11,13,\\dots$).</p>'
+'<p><b>Составное число</b> имеет <b>больше двух</b> делителей ($4,6,8,9,\\dots$).</p>'
+'<p>Число $1$ — <b>ни простое, ни составное</b> (у него один делитель). $2$ — единственное чётное простое.</p>');
h+=makeCard('rule','Разложение на простые множители','14.2',
'<p>Любое составное число можно представить как произведение простых множителей — единственным образом. Делят на наименьшие простые: $2,3,5,7,\\dots$</p>');
h+=makeCard('example','Разбор по шагам','14.3',
'<p>Разложим $60$ на простые множители.</p>'
+'<ol style="padding-left:22px;line-height:2">'
+'<li>$60:2=30$.</li>'
+'<li>$30:2=15$.</li>'
+'<li>$15:3=5$.</li>'
+'<li>$5$ — простое. Итог: $60=2\\cdot2\\cdot3\\cdot5=2^2\\cdot3\\cdot5$.</li>'
+'</ol>');
h+=makeCard('theory','А знаешь ли ты?','14.4',
'<p>Простых чисел бесконечно много — это доказал ещё Евклид. А самое большое известное простое число содержит более $41$ миллиона цифр! Его нашли в $2024$ году с помощью тысяч компьютеров по всему миру.</p>');
h+='<div class="wg" id="p14-iv1"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><div class="wg-title">Решето Эратосфена</div></div>'
+'<div class="wg-help">Нажми на все <b>простые</b> числа от 2 до 30, затем проверь себя.</div>'
+'<div id="p14-grid" style="display:grid;grid-template-columns:repeat(8,1fr);gap:6px;max-width:360px;margin:8px auto"></div>'
+'<div class="actions" style="justify-content:center"><button class="btn primary" id="p14-chk">Проверить</button><button class="btn" id="p14-rst">Сбросить</button></div>'
+'<div class="feedback" id="p14-fb"></div></div>';
h+='<div class="wg" id="p14-iv2"><div class="wg-header"><span class="wg-badge">Интерактив 2</span><div class="wg-title">Простое или составное?</div></div>'
+'<div class="wg-help">Определи, простое число или составное.</div>'
+'<div class="score-display"><span>Вопрос <b id="p14-i">1</b> / 6</span><span>Очки: <b id="p14-s">0</b> / 6</span></div>'
+'<div id="p14-q" class="qbox"></div>'
+'<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap"><button class="btn primary" data-p="1">простое</button><button class="btn primary" data-p="0">составное</button></div>'
+'<div class="feedback" id="p14-qfb"></div></div>';
h+=secNav('p13','p15')+readBtn('p14');
box.innerHTML=h; renderMath(box);
function isPrime(n){ if(n<2)return false; for(var k=2;k*k<=n;k++)if(n%k===0)return false; return true; }
(function(){
var sel={};
var grid=document.getElementById('p14-grid'),html='';
for(var n=2;n<=30;n++) html+='<button class="btn" data-n="'+n+'" style="padding:8px 0;font-weight:700">'+n+'</button>';
grid.innerHTML=html;
grid.querySelectorAll('[data-n]').forEach(function(b){ b.addEventListener('click',function(){ var n=+b.getAttribute('data-n'); if(sel[n]){ delete sel[n]; b.classList.remove('primary'); } else { sel[n]=1; b.classList.add('primary'); } }); });
document.getElementById('p14-chk').addEventListener('click',function(){ var fb=document.getElementById('p14-fb'),ok=0,bad=0,miss=0;
for(var n=2;n<=30;n++){ var p=isPrime(n),s=!!sel[n]; if(p&&s)ok++; else if(!p&&s)bad++; else if(p&&!s)miss++; }
if(bad===0&&miss===0){ feedback(fb,true,'✓ Идеально! Все простые числа до 30 найдены. +15 XP'); addXp(15,'p14-iv1'); bumpProgress('p14',35); }
else feedback(fb,false,'✗ Верных: '+ok+'. Лишних: '+bad+', пропущено: '+miss+'. Простые до 30: 2,3,5,7,11,13,17,19,23,29.'); });
document.getElementById('p14-rst').addEventListener('click',function(){ sel={}; grid.querySelectorAll('[data-n]').forEach(function(b){ b.classList.remove('primary'); }); document.getElementById('p14-fb').style.display='none'; });
})();
(function(){
var i=0,score=0,cur=null;
function gen(){ var n; if(_ri(0,1)===0){ var pr=[2,3,5,7,11,13,17,19,23,29,31,37,41,43]; n=_pick(pr); } else { n=_ri(4,49); while(isPrime(n))n=_ri(4,49); } cur={n:n,prime:isPrime(n)}; }
function show(){ if(i>=6){ document.getElementById('p14-q').innerHTML='<b>Готово! '+score+' / 6</b>'; if(score>=5){addXp(15,'p14-iv2');bumpProgress('p14',30);}else if(score>=3){addXp(8,'p14-iv2');bumpProgress('p14',16);} return; }
gen(); document.getElementById('p14-i').textContent=i+1;
document.getElementById('p14-q').innerHTML='Число $'+cur.n+'$ — простое или составное?'; renderMath(document.getElementById('p14-q'));
document.getElementById('p14-qfb').style.display='none'; }
function ans(p){ if(i>=6)return; var fb=document.getElementById('p14-qfb');
if((p===1)===cur.prime){ score++; feedback(fb,true,'✓ Верно! '+cur.n+' — '+(cur.prime?'простое':'составное')+'.'); } else feedback(fb,false,'✗ Нет. '+cur.n+' — '+(cur.prime?'простое':'составное')+'.');
document.getElementById('p14-s').textContent=score; i++; setTimeout(show,1200); }
document.querySelectorAll('#p14-iv2 [data-p]').forEach(function(b){ b.addEventListener('click',function(){ ans(+b.getAttribute('data-p')); }); });
show();
})();
}
/* ===================== ДАННЫЕ САЙДБАРА / ГЛОССАРИЯ ===================== */
var SIDEBARS = {
p1:{ title:'Шпаргалка § 1', rows:[
@@ -1112,6 +1251,16 @@ var SIDEBARS = {
['Кратное','делится на число нацело'],
['НОД','наибольший общий делитель'],
['НОК','наименьшее общее кратное'] ]},
p13:{ title:'Шпаргалка § 13', rows:[
['на 2','последняя цифра чётная'],
['на 5 / 10','оканчивается на 0/5 · на 0'],
['на 3 / 9','сумма цифр делится на 3 / 9'],
['на 4','две последние делятся на 4'] ]},
p14:{ title:'Шпаргалка § 14', rows:[
['Простое','ровно 2 делителя'],
['Составное','больше 2 делителей'],
['1','ни простое, ни составное'],
['Разложение','$60=2^2\\cdot3\\cdot5$'] ]},
final:{ title:'Финал главы 1', rows:[
['5 боссов','разряды, округление, действия, степень'],
['Победа','4 из 5 и больше'],
@@ -1131,7 +1280,7 @@ var GLOSSARY = [
{ term:'разряд', def:'Место цифры в записи числа: единицы, десятки, сотни и т. д.', sec:'p2', aliases:['разряд','разряда','разряде','разряды','разрядов'] },
{ term:'класс', def:'Группа из трёх соседних разрядов: единицы, тысячи, миллионы.', sec:'p2', aliases:['класс','класса','классе','классы','классов'] }
];
var BUILDERS = { p1:buildP1, p2:buildP2, p3:buildP3, p4:buildP4, p5:buildP5, p6:buildP6, p7:buildP7, p8:buildP8, p9:buildP9, p10:buildP10, p11:buildP11, p12:buildP12, final:buildFinal };
var BUILDERS = { p1:buildP1, p2:buildP2, p3:buildP3, p4:buildP4, p5:buildP5, p6:buildP6, p7:buildP7, p8:buildP8, p9:buildP9, p10:buildP10, p11:buildP11, p12:buildP12, p13:buildP13, p14:buildP14, final:buildFinal };
Object.assign(window.M6, { sidebars:SIDEBARS, tips:TIPS, glossary:GLOSSARY, builders:BUILDERS });
</script>