A classic application: Ramsey-type coloring. Color each edge of a complete graph on n nodes red or blue at random (fair coin per edge). A monochromatic clique is a set of nodes all connected by the same color.
<!-- Probabilistic method demo: random 2-coloring of K_n edges -->
<p class="hint">Color the edges of a complete graph randomly (red or blue). A <b>monochromatic triangle</b> is three nodes all connected by the same color. Hit <b>Randomize</b> to pick a random 2-coloring. The probabilistic method says: if the probability of avoiding all monochromatic triangles is positive, a good coloring <em>must</em> exist.</p>
<div class="controls">
<label for="nNodes">Nodes n = <strong id="nVal">5</strong></label>
<input id="nNodes" type="range" min="4" max="8" value="5" />
</div>
<canvas id="canvas" width="340" height="220"></canvas>
<div class="status" id="status"></div>
<div class="stats" id="stats"></div>
<div class="btns">
<button id="btnRand" type="button">Randomize</button>
<button id="btnReset" type="button" class="ghost">Clear</button>
</div>
/* Styles for the random coloring demo */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .6rem; line-height: 1.45; }
.controls { display: flex; align-items: center; gap: .8rem; margin-bottom: .5rem; font-size: .9rem; }
input[type=range] { flex: 1; max-width: 160px; }
canvas { display: block; background: #f5f7f9; border-radius: 10px; border: 1px solid #dde3ea; }
.status { font-size: 1rem; font-weight: 600; margin: .4rem 0 .2rem; min-height: 1.3em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.stats { font-size: .82rem; color: #555; margin-bottom: .5rem; min-height: 1.2em; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Probabilistic method: random 2-coloring of complete graph K_n
// Detect monochromatic triangles to illustrate the existence argument
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const statusEl = document.getElementById('status');
const statsEl = document.getElementById('stats');
const nInput = document.getElementById('nNodes');
const nValEl = document.getElementById('nVal');
// Edge colors: red or blue
const RED = '#e63946';
const BLUE = '#1d6fa5';
const NODE_COLOR = '#1d3557';
let n = 5;
let edges = {}; // maps edgeKey -> color
// Place n nodes evenly on a circle
function nodePositions(n, cx, cy, r) {
const pos = [];
for (let i = 0; i < n; i++) {
const angle = (2 * Math.PI * i / n) - Math.PI / 2;
pos.push({ x: cx + r * Math.cos(angle), y: cy + r * Math.sin(angle) });
}
return pos;
}
function edgeKey(i, j) { return i < j ? i + '-' + j : j + '-' + i; }
// Assign each edge a random color
function randomize() {
edges = {};
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
// fair coin flip
edges[edgeKey(i, j)] = Math.random() < 0.5 ? 'red' : 'blue';
}
}
draw();
analyze();
}
// Render nodes and colored edges on canvas
function draw() {
const W = canvas.width, H = canvas.height;
ctx.clearRect(0, 0, W, H);
const pos = nodePositions(n, W / 2, H / 2, Math.min(W, H) * 0.36);
// draw edges first so nodes sit on top
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
const k = edgeKey(i, j);
ctx.beginPath();
ctx.moveTo(pos[i].x, pos[i].y);
ctx.lineTo(pos[j].x, pos[j].y);
ctx.strokeStyle = edges[k] === 'red' ? RED : BLUE;
ctx.lineWidth = 2.4;
ctx.stroke();
}
}
// draw nodes with labels
for (let i = 0; i < n; i++) {
ctx.beginPath();
ctx.arc(pos[i].x, pos[i].y, 10, 0, 2 * Math.PI);
ctx.fillStyle = NODE_COLOR;
ctx.fill();
ctx.fillStyle = '#fff';
ctx.font = '700 11px system-ui';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(i + 1, pos[i].x, pos[i].y);
}
}
// Count monochromatic triangles and update status
function analyze() {
if (Object.keys(edges).length === 0) {
statusEl.textContent = '';
statusEl.className = 'status';
statsEl.textContent = '';
return;
}
// enumerate all triangles and check for same color
const mono = [];
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
for (let k = j + 1; k < n; k++) {
const c1 = edges[edgeKey(i, j)];
const c2 = edges[edgeKey(i, k)];
const c3 = edges[edgeKey(j, k)];
if (c1 === c2 && c2 === c3) {
mono.push([i + 1, j + 1, k + 1, c1]);
}
}
}
}
const totalTriangles = n * (n - 1) * (n - 2) / 6;
const expected = (totalTriangles / 4).toFixed(2);
if (mono.length === 0) {
statusEl.textContent = '✓ No monochromatic triangle — this coloring works!';
statusEl.className = 'status ok';
} else {
const list = mono.map(([a, b, c, col]) =>
'{' + a + ',' + b + ',' + c + '} (' + (col === 'red' ? 'red' : 'blue') + ')'
).join(', ');
statusEl.textContent = fmt('✗ Monochromatic triangles: %list%', { list: list });
statusEl.className = 'status bad';
}
statsEl.textContent = fmt('Total triangles: %totalTriangles% · Expected monochromatic: %expected% · Found: %length%.', { totalTriangles: totalTriangles, expected: expected, length: mono.length });
}
nInput.addEventListener('input', () => {
n = parseInt(nInput.value, 10);
nValEl.textContent = n;
edges = {};
draw();
analyze();
});
document.getElementById('btnRand').addEventListener('click', randomize);
document.getElementById('btnReset').addEventListener('click', () => {
edges = {};
draw();
analyze();
});
// initial empty state
draw();
analyze();
<!-- Probabilistic method demo: random 2-coloring of K_n edges -->
<p class="hint">Color the edges of a complete graph randomly (red or blue). A <b>monochromatic triangle</b> is three nodes all connected by the same color. Hit <b>Randomize</b> to pick a random 2-coloring. The probabilistic method says: if the probability of avoiding all monochromatic triangles is positive, a good coloring <em>must</em> exist.</p>
<div class="controls">
<label for="nNodes">Nodes n = <strong id="nVal">5</strong></label>
<input id="nNodes" type="range" min="4" max="8" value="5" />
</div>
<canvas id="canvas" width="340" height="220"></canvas>
<div class="status" id="status"></div>
<div class="stats" id="stats"></div>
<div class="btns">
<button id="btnRand" type="button">Randomize</button>
<button id="btnReset" type="button" class="ghost">Clear</button>
</div>
/* Styles for the random coloring demo */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .6rem; line-height: 1.45; }
.controls { display: flex; align-items: center; gap: .8rem; margin-bottom: .5rem; font-size: .9rem; }
input[type=range] { flex: 1; max-width: 160px; }
canvas { display: block; background: #f5f7f9; border-radius: 10px; border: 1px solid #dde3ea; }
.status { font-size: 1rem; font-weight: 600; margin: .4rem 0 .2rem; min-height: 1.3em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.stats { font-size: .82rem; color: #555; margin-bottom: .5rem; min-height: 1.2em; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Probabilistic method: random 2-coloring of complete graph K_n
// Detect monochromatic triangles to illustrate the existence argument
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const statusEl = document.getElementById('status');
const statsEl = document.getElementById('stats');
const nInput = document.getElementById('nNodes');
const nValEl = document.getElementById('nVal');
// Edge colors: red or blue
const RED = '#e63946';
const BLUE = '#1d6fa5';
const NODE_COLOR = '#1d3557';
let n = 5;
let edges = {}; // maps edgeKey -> color
// Place n nodes evenly on a circle
function nodePositions(n, cx, cy, r) {
const pos = [];
for (let i = 0; i < n; i++) {
const angle = (2 * Math.PI * i / n) - Math.PI / 2;
pos.push({ x: cx + r * Math.cos(angle), y: cy + r * Math.sin(angle) });
}
return pos;
}
function edgeKey(i, j) { return i < j ? i + '-' + j : j + '-' + i; }
// Assign each edge a random color
function randomize() {
edges = {};
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
// fair coin flip
edges[edgeKey(i, j)] = Math.random() < 0.5 ? 'red' : 'blue';
}
}
draw();
analyze();
}
// Render nodes and colored edges on canvas
function draw() {
const W = canvas.width, H = canvas.height;
ctx.clearRect(0, 0, W, H);
const pos = nodePositions(n, W / 2, H / 2, Math.min(W, H) * 0.36);
// draw edges first so nodes sit on top
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
const k = edgeKey(i, j);
ctx.beginPath();
ctx.moveTo(pos[i].x, pos[i].y);
ctx.lineTo(pos[j].x, pos[j].y);
ctx.strokeStyle = edges[k] === 'red' ? RED : BLUE;
ctx.lineWidth = 2.4;
ctx.stroke();
}
}
// draw nodes with labels
for (let i = 0; i < n; i++) {
ctx.beginPath();
ctx.arc(pos[i].x, pos[i].y, 10, 0, 2 * Math.PI);
ctx.fillStyle = NODE_COLOR;
ctx.fill();
ctx.fillStyle = '#fff';
ctx.font = '700 11px system-ui';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(i + 1, pos[i].x, pos[i].y);
}
}
// Count monochromatic triangles and update status
function analyze() {
if (Object.keys(edges).length === 0) {
statusEl.textContent = '';
statusEl.className = 'status';
statsEl.textContent = '';
return;
}
// enumerate all triangles and check for same color
const mono = [];
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
for (let k = j + 1; k < n; k++) {
const c1 = edges[edgeKey(i, j)];
const c2 = edges[edgeKey(i, k)];
const c3 = edges[edgeKey(j, k)];
if (c1 === c2 && c2 === c3) {
mono.push([i + 1, j + 1, k + 1, c1]);
}
}
}
}
const totalTriangles = n * (n - 1) * (n - 2) / 6;
const expected = (totalTriangles / 4).toFixed(2);
if (mono.length === 0) {
statusEl.textContent = '✓ No monochromatic triangle — this coloring works!';
statusEl.className = 'status ok';
} else {
const list = mono.map(([a, b, c, col]) =>
'{' + a + ',' + b + ',' + c + '} (' + (col === 'red' ? 'red' : 'blue') + ')'
).join(', ');
statusEl.textContent = fmt('✗ Monochromatic triangles: %list%', { list: list });
statusEl.className = 'status bad';
}
statsEl.textContent = fmt('Total triangles: %totalTriangles% · Expected monochromatic: %expected% · Found: %length%.', { totalTriangles: totalTriangles, expected: expected, length: mono.length });
}
nInput.addEventListener('input', () => {
n = parseInt(nInput.value, 10);
nValEl.textContent = n;
edges = {};
draw();
analyze();
});
document.getElementById('btnRand').addEventListener('click', randomize);
document.getElementById('btnReset').addEventListener('click', () => {
edges = {};
draw();
analyze();
});
// initial empty state
draw();
analyze();
For small n the expected number of monochromatic triangles is (3n)⋅2⋅(1/2)3=(3n)/4. For n=4 that is just 1, so some colorings will avoid them entirely — and the probabilistic method says: because the probability of avoiding them is positive, a good coloring exists. Hit Randomize several times and watch.
Comments
Loading comments...