Skill: multi-step-equations
Worksheet: Multi-Step Equations
Print a fresh 20-problem worksheet any time — Easy, Medium, or Hard, with a hidden answer key.
Generates 20 fresh problems on every click. The answer key stays hidden until you reveal it.
Multi-Step Equations
Answer Key
For the teacher or for self-checking after you finish.
// __QCORE_BEGIN__
var QGEN = (function(){
'use strict';
function ri(rng, lo, hi){ return lo + Math.floor(rng() * (hi - lo + 1)); }
function pick(rng, a){ return a[Math.floor(rng() * a.length)]; }
function mhtml(s){ return '' + s + ''; }
function sgn(n){ return n < 0 ? '−' + (-n) : String(n); }
function showX(a){ return a === 1 ? 'x' : a === -1 ? '−x' : sgn(a) + 'x'; }
function sideHtml(a, b){
var xs = showX(a);
if (b === 0) return xs;
return xs + (b > 0 ? ' + ' + b : ' − ' + (-b));
}
function fracHtml(n, d){
return '' + n + '' + d + '';
}
var T = [];
function eqBoth(rng, a, b, c, x, level){
var d = (a - c) * x + b;
var q = {q:'Solve: ' + mhtml(sideHtml(a, b) + ' = ' + sideHtml(c, d)) + '.',
a:String(x), av:x, alt:['x=' + x, 'x = ' + x],
data:{kind:'eqm', form:'both', a:a, b:b, c:c, d:d, x:x},
hint:'Subtract the smaller x-term from both sides, then solve the two-step.'};
var kx = a - c, kn = d - b;
q.steps = [
'Move x-terms together: ' + mhtml(showX(kx) + ' = ' + kn),
'Divide by ' + kx + ': x = ' + x + '.',
'Check: ' + (a * x + b) + ' = ' + (c * x + d) + '.'];
return q;
}
/* easy: small coefficients, positive */
T.push({id:'both1', name:'Variables both sides', level:'easy', gen:function(rng){
var x = ri(rng, 1, 6), a = ri(rng, 2, 5), c = ri(rng, 1, a - 1), b = ri(rng, 0, 5);
return eqBoth(rng, a, b, c, x, 'easy');
}});
/* medium: negatives, bigger */
T.push({id:'both2', name:'Both sides, trickier', level:'medium', gen:function(rng){
var x = ri(rng, -5, 5);
if (x === 0) x = 3;
var a = pick(rng, [2, 3, 4, 5, -2, -3]);
var c = pick(rng, [1, 2, 3, 4, -1, -2]);
if (c === a) c = 1;
var b = ri(rng, -6, 6);
return eqBoth(rng, a, b, c, x, 'medium');
}});
/* medium: distribute first */
T.push({id:'paren', name:'Parentheses first', level:'medium', gen:function(rng){
var k = ri(rng, 2, 3), bi = ri(rng, 1, 3), ci = ri(rng, -4, 4);
if (ci === 0) ci = 2;
var x = ri(rng, 1, 6), d = ri(rng, 1, k * bi - 1);
var e = k * (bi * x + ci) - d * x;
var lhs = k + '(' + sideHtml(bi, ci) + ')';
return {q:'Solve: ' + mhtml(lhs + ' = ' + sideHtml(d, e)) + '.',
a:String(x), av:x, alt:['x=' + x, 'x = ' + x],
data:{kind:'eqm', form:'paren', a:k * bi, b:k * ci, c:d, d:e, x:x},
hint:'Distribute the ' + k + ' first, then collect x-terms.',
steps:['Distribute: ' + mhtml(sideHtml(k * bi, k * ci) + ' = ' + sideHtml(d, e)),
'Collect: ' + mhtml(showX(k * bi - d) + ' = ' + (e - k * ci)),
'Answer: x = ' + x + '. Check: ' + (k * (bi * x + ci)) + ' = ' + (d * x + e) + '.']};
}});
/* hard: no solution or infinite */
T.push({id:'special', name:'Special cases', level:'hard', gen:function(rng){
var kind = pick(rng, ['none', 'infinite']);
var a = ri(rng, 2, 5), b = a * ri(rng, 1, 3);
if (kind === 'none'){
var d2 = b + pick(rng, [1, 2, 3, -2, -3]);
return {q:'How many solutions does ' + mhtml(sideHtml(a, b) + ' = ' + sideHtml(a, d2)) + ' have? (Answer: no solution or infinitely many.)',
a:'no solution', av:null, alt:['none', 'no solutions'],
data:{kind:'eqm', form:'special', a:a, b:b, c:a, d:d2, special:'none'},
hint:'Subtract ' + showX(a) + ' from both sides. What is left?',
steps:['Subtract ' + showX(a) + ' from both sides: ' + mhtml(b + ' = ' + d2),
'False statement — no solution.']};
}
return {q:'How many solutions does ' + mhtml(sideHtml(a, b) + ' = ' + a + '(' + sideHtml(1, b / a) + '))') + ' have? (Answer: no solution or infinitely many.)',
a:'infinitely many', av:null, alt:['infinite', 'infinite solutions', 'all real numbers'],
data:{kind:'eqm', form:'special', a:a, b:b, c:a, d:b, special:'infinite'},
hint:'Distribute the right side. Do the two sides match exactly?',
steps:['Distribute: right side = ' + mhtml(sideHtml(a, b)),
'Identical sides — infinitely many solutions.']};
}});
/* hard: fraction coefficient */
T.push({id:'frac', name:'Fractions', level:'hard', gen:function(rng){
var qn = pick(rng, [2, 3, 4]), pn = ri(rng, 1, qn - 1);
var x = ri(rng, 2, 6) * qn;
var b = ri(rng, 1, 6), c = Math.round(pn * x / qn) + b;
return {q:'Solve: ' + fracHtml(pn, qn) + mhtml('x + ' + b + ' = ' + c) + '.',
a:String(x), av:x, alt:['x=' + x, 'x = ' + x],
data:{kind:'eqm', form:'frac', a:pn / qn, b:b, c:0, d:c, x:x},
hint:'Multiply every term by ' + qn + ' to clear the fraction.',
steps:['Multiply by ' + qn + ': ' + mhtml(pn + 'x + ' + (b * qn) + ' = ' + (c * qn)),
'Subtract ' + (b * qn) + ', divide by ' + pn + ': x = ' + x + '.',
'Check: ' + (pn * x / qn) + ' + ' + b + ' = ' + c + '.']};
}});
/* hard: decimals */
T.push({id:'dec', name:'Decimals', level:'hard', gen:function(rng){
var x = ri(rng, 1, 5);
var p = pick(rng, [0.5, 1.5, 2.5]), q = p + pick(rng, [1, 2]);
var b = ri(rng, 1, 6);
/* equation: p x + b = q x + e -> e = p*x + b - q*x */
var e2 = Math.round((p * x + b - q * x) * 10) / 10;
function f(n){ return String(Math.round(n * 10) / 10); }
return {q:'Solve: ' + mhtml(f(p) + 'x + ' + b + ' = ' + f(q) + 'x + ' + f(e2)) + '.',
a:String(x), av:x, alt:['x=' + x, 'x = ' + x],
data:{kind:'eqm', form:'dec', a:p, b:b, c:q, d:e2, x:x},
hint:'Subtract the smaller decimal x-term from both sides.',
steps:['Collect x-terms: ' + mhtml(f(q - p) + 'x = ' + f(b - e2)),
'Divide: x = ' + x + '.',
'Check: ' + f(p * x + b) + ' = ' + f(q * x + e2) + '.']};
}});
function norm(s){ return String(s).trim().toLowerCase().replace(/\s+/g, ''); }
function checkAnswer(user, canonical, av, alt){
var s = norm(user), c0 = norm(canonical);
if (c0 === 'nosolution'){
if (s === 'nosolution' || s === 'none' || s === 'nosolutions') return true;
}
if (c0 === 'infinitelymany'){
if (s === 'infinitelymany' || s === 'infinite' || s === 'infinitesolutions' || s === 'allrealnumbers' || s === 'allreal' || s === 'allx') return true;
}
s = s.replace(/^x=/, '');
for (var i = 0; i < (alt || []).length; i++){
if (s === norm(alt[i]).replace(/^x=/, '')) return true;
}
if (!/^-?(\d+(\.\d+)?|\.\d+)$/.test(s)) return false;
if (av === null || av === undefined) return false;
return Math.abs(parseFloat(s) - av) < 1e-9;
}
return {templates:T, checkAnswer:checkAnswer};
})();
// __QCORE_END__
/* Shared worksheet engine (Wave 2C). Requires QGEN global. */ (function(){ 'use strict'; var COUNT = 20; function $(id){ return document.getElementById(id); } function esc(s){ return String(s).replace(/\x26/g,'&').replace(//g,'>'); } function templatesFor(level){ var Ts = QGEN.templates.filter(function(t){ return t.level === level; }); return Ts.length ? Ts : QGEN.templates.slice(); } function generate(){ var level = $('wLevel').value; var Ts = templatesFor(level); var probs = $('wProblems'), keyg = $('wKeyGrid'); probs.innerHTML = ''; keyg.innerHTML = ''; for (var i = 0; i < COUNT; i++){ var t = Ts[i % Ts.length]; var q = t.gen(Math.random); var d = document.createElement('div'); d.className = 'prob'; d.innerHTML = '' + (i + 1) + '' + q.q + ''; probs.appendChild(d); var k = document.createElement('div'); k.className = 'krow'; k.innerHTML = '' + (i + 1) + '' + esc(q.a) + ''; keyg.appendChild(k); } $('wKey').hidden = !$('wKeyToggle').getAttribute('aria-pressed') || $('wKeyToggle').getAttribute('aria-pressed') === 'false'; var lbl = {easy:'Easy', medium:'Medium', hard:'Hard'}[level] || level; $('wCount').textContent = COUNT + ' problems · ' + lbl; } document.addEventListener('DOMContentLoaded', function(){ $('wGen').addEventListener('click', generate); $('wPrint').addEventListener('click', function(){ window.print(); }); $('wKeyToggle').addEventListener('click', function(){ var on = this.getAttribute('aria-pressed') === 'true'; this.setAttribute('aria-pressed', String(!on)); this.textContent = on ? 'Show answer key' : 'Hide answer key'; $('wKey').hidden = on; }); generate(); }); })();