feat(trainer): красивое решение НОД/НОК — через разложение на простые множители
- НОД/НОК переписаны: число строится из двух простых p<q (из {2,3,5,7}) со степенями 1..2 (a=p^e1·q^f1) → разложение известно из параметров. Решение показывает СТАНДАРТНЫЙ школьный метод: разложить оба числа, НОД = общие множители в наименьших степенях, НОК = все в наибольших. Пример: 225=3²·5², 45=3²·5 → НОД=3²·5=45
- выбор простого — тернарником в derive (ip/iq, НЕ pi — pi это π в SimExpr!)
- exprToLatex: x^1→x, x^0→1 (чтобы 7^1 печаталось как 7) + ставит · между числовыми множителями (2·7², а не слипшееся «27²»); алгебраическое неявное умножение (2x, 3(x+1)) сохранено
- gcd/lcm дают эталон для проверки, min/max — степени для шагов
- смоук движка 1154/1154, страница 40/40; эмодзи 0
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -160,6 +160,11 @@
|
||||
var s = _latex(node);
|
||||
return _prec(node) < minPrec ? '\\left(' + s + '\\right)' : s;
|
||||
}
|
||||
// «числовой множитель»: число, константа или степень с числовым основанием (2, 7^2).
|
||||
// Между двумя такими ставим ·, иначе адъяцентность «2·7²» прочитается как «27²».
|
||||
function _isNumFactor(n) {
|
||||
return n.k === 'num' || n.k === 'const' || (n.k === 'bin' && n.op === '^' && _isNumFactor(n.a));
|
||||
}
|
||||
// Операнд умножения: отрицательное/унарное/сумму берём в скобки, иначе
|
||||
// соседство схлопнет смысл (7*(-5) -> «7-5», 6*(x+1) -> «6x+1»).
|
||||
function _mulOperand(node) {
|
||||
@@ -197,6 +202,8 @@
|
||||
if (op === '/') return '\\frac{' + _latex(node.a) + '}{' + _latex(node.b) + '}';
|
||||
if (op === '^') {
|
||||
var base = _prec(node.a) < 5 ? '\\left(' + _latex(node.a) + '\\right)' : _latex(node.a);
|
||||
if (node.b.k === 'num' && node.b.v === 1) return base; // x^1 -> x
|
||||
if (node.b.k === 'num' && node.b.v === 0) return '1'; // x^0 -> 1
|
||||
return base + '^{' + _latex(node.b) + '}';
|
||||
}
|
||||
if (op === '*') {
|
||||
@@ -206,7 +213,8 @@
|
||||
if (node.b.k === 'num' && Math.abs(node.b.v) === 1 && node.a.k !== 'num')
|
||||
return (node.b.v < 0 ? '-' : '') + _mulOperand(node.a);
|
||||
if (_isNeg(node.a)) return '-' + _latex({ k: 'bin', op: '*', a: _negate(node.a), b: node.b }); // -5*x -> «-5x»
|
||||
var sep = (node.b.k === 'num' && node.b.v >= 0) ? ' \\cdot ' : ''; // знак · между числами; иначе соседство
|
||||
// · между числами и числовыми множителями (2·3, 2·7²); иначе соседство (2x, 3(x+1))
|
||||
var sep = ((node.b.k === 'num' && node.b.v >= 0) || (_isNumFactor(node.a) && _isNumFactor(node.b))) ? ' \\cdot ' : '';
|
||||
return _mulOperand(node.a) + sep + _mulOperand(node.b);
|
||||
}
|
||||
if (op === '%') return _wrapL(node.a, 2) + ' \\bmod ' + _wrapL(node.b, 3);
|
||||
|
||||
@@ -835,34 +835,57 @@
|
||||
},
|
||||
|
||||
/* ═══ Тема: НОД и НОК (5–6 класс) ═══
|
||||
Числа строим как g·m и g·k (общий множитель g) → НОД ≥ g, не тривиален.
|
||||
gcd/lcm — функции SimExpr (алгоритм Евклида), считаются и проверяются движком. */
|
||||
Оба числа строим из ДВУХ простых множителей p<q (из {2,3,5,7}) с показателями
|
||||
1..2: a = p^e1·q^f1, b = p^e2·q^f2. Тогда разложение известно из параметров →
|
||||
решение показывает СТАНДАРТНЫЙ школьный метод (разложение на простые множители):
|
||||
НОД = общие множители в НАИМЕНЬШИХ степенях (min показателей),
|
||||
НОК = все множители в НАИБОЛЬШИХ степенях (max показателей).
|
||||
gcd/lcm — функции SimExpr, дают эталон для проверки; min/max — показатели для шагов.
|
||||
(exprToLatex убирает ^1 → разложение печатается красиво: 2^2·3, а не 2^2·3^1.) */
|
||||
|
||||
/* наибольший общий делитель */
|
||||
/* наибольший общий делитель — через разложение на простые множители */
|
||||
{
|
||||
id: 'gcd-pair', topic: 'gcd-lcm', order: 1, subject: 'algebra', grade: 5, kind: 'compute',
|
||||
title: 'НОД двух чисел',
|
||||
pick: { g: [2, 9], m: [2, 8], k: [2, 8] }, constraint: 'm != k',
|
||||
derive: { a: 'g*m', b: 'g*k', val: 'gcd(a, b)' },
|
||||
pick: { ip: [0, 3], iq: [0, 3], e1: [1, 2], e2: [1, 2], f1: [1, 2], f2: [1, 2] },
|
||||
constraint: 'ip < iq',
|
||||
derive: {
|
||||
p: '(ip==0)?2:((ip==1)?3:((ip==2)?5:7))',
|
||||
q: '(iq==0)?2:((iq==1)?3:((iq==2)?5:7))',
|
||||
a: 'p^e1 * q^f1', b: 'p^e2 * q^f2',
|
||||
me: 'min(e1, e2)', mf: 'min(f1, f2)', val: 'gcd(a, b)'
|
||||
},
|
||||
require: 'a != b && a <= 400 && b <= 400',
|
||||
lhs: 'x', rhs: 'gcd({a}, {b})', display: 'Найдите наибольший общий делитель (НОД) чисел {a} и {b}.',
|
||||
answerVar: 'x', answer: 'val', integerAnswer: true,
|
||||
solution: [
|
||||
{ note: 'Разложите числа на простые множители; НОД — произведение общих множителей в наименьших степенях (или алгоритм Евклида).', tex: '' },
|
||||
{ note: 'Получаем:', tex: 'x = {ans}' }
|
||||
{ note: 'Разложим {a} на простые множители:', tex: '{a} = {p}^{e1} * {q}^{f1}' },
|
||||
{ note: 'Разложим {b} на простые множители:', tex: '{b} = {p}^{e2} * {q}^{f2}' },
|
||||
{ note: 'НОД — произведение ОБЩИХ простых множителей в НАИМЕНЬШИХ степенях:', tex: 'x = {p}^{me} * {q}^{mf}' },
|
||||
{ note: 'Перемножаем:', tex: 'x = {ans}' }
|
||||
]
|
||||
},
|
||||
|
||||
/* наименьшее общее кратное */
|
||||
/* наименьшее общее кратное — через разложение на простые множители */
|
||||
{
|
||||
id: 'lcm-pair', topic: 'gcd-lcm', order: 2, subject: 'algebra', grade: 6, kind: 'compute',
|
||||
title: 'НОК двух чисел',
|
||||
pick: { g: [2, 6], m: [2, 7], k: [2, 7] }, constraint: 'm != k',
|
||||
derive: { a: 'g*m', b: 'g*k', val: 'lcm(a, b)' },
|
||||
pick: { ip: [0, 3], iq: [0, 3], e1: [1, 2], e2: [1, 2], f1: [1, 2], f2: [1, 2] },
|
||||
constraint: 'ip < iq',
|
||||
derive: {
|
||||
p: '(ip==0)?2:((ip==1)?3:((ip==2)?5:7))',
|
||||
q: '(iq==0)?2:((iq==1)?3:((iq==2)?5:7))',
|
||||
a: 'p^e1 * q^f1', b: 'p^e2 * q^f2',
|
||||
Me: 'max(e1, e2)', Mf: 'max(f1, f2)', val: 'lcm(a, b)'
|
||||
},
|
||||
require: 'a != b && a <= 400 && b <= 400',
|
||||
lhs: 'x', rhs: 'lcm({a}, {b})', display: 'Найдите наименьшее общее кратное (НОК) чисел {a} и {b}.',
|
||||
answerVar: 'x', answer: 'val', integerAnswer: true,
|
||||
solution: [
|
||||
{ note: 'НОК = произведение чисел, делённое на их НОД: НОК(a, b) = a·b ÷ НОД(a, b).', tex: '' },
|
||||
{ note: 'Получаем:', tex: 'x = {ans}' }
|
||||
{ note: 'Разложим {a} на простые множители:', tex: '{a} = {p}^{e1} * {q}^{f1}' },
|
||||
{ note: 'Разложим {b} на простые множители:', tex: '{b} = {p}^{e2} * {q}^{f2}' },
|
||||
{ note: 'НОК — произведение ВСЕХ простых множителей в НАИБОЛЬШИХ степенях:', tex: 'x = {p}^{Me} * {q}^{Mf}' },
|
||||
{ note: 'Перемножаем:', tex: 'x = {ans}' }
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user