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:
Maxim Dolgolyov
2026-06-25 17:39:21 +03:00
parent a7d20a0c90
commit 664751e273
2 changed files with 44 additions and 13 deletions
+9 -1
View File
@@ -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);