update pigeonhole
This commit is contained in:
173
app/templates/pigeonhole.html
Normal file
173
app/templates/pigeonhole.html
Normal file
@@ -0,0 +1,173 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Pigeonhole Principle Simulation{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="max-w-screen grid grid-cols-1 items-center divide-y p-6">
|
||||
<div class="flex flex-col justify-center text-center">
|
||||
<h1 class="text-3xl font-bold mb-4">Pigeonhole Principle Simulation</h1>
|
||||
<p class="text-lg text-gray-700 dark:text-gray-300 mb-2">
|
||||
If we place 5 dots in a square of side length $s$, there must be at least two dots whose distance $d$
|
||||
satisfies:
|
||||
$$ d < \frac{s}{\sqrt{2}} $$ </p>
|
||||
<p class="text-md text-gray-600 dark:text-gray-400 mb-6">
|
||||
(The user requested condition: "distance is smaller than square root of square ($s^2$)", which means
|
||||
$d < \sqrt{s^2}=s$. Since $\frac{s}{\sqrt{2}} \approx 0.707 s < s$, the condition is always met if
|
||||
the stronger Pigeonhole Principle holds.) </p>
|
||||
|
||||
<div class="flex justify-center mb-4">
|
||||
<button onclick="generatePoints()"
|
||||
class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition">
|
||||
Generate New Points
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center justify-center pt-6">
|
||||
<div class="relative">
|
||||
<canvas id="pigeonholeCanvas" width="500" height="500"
|
||||
class="border-2 border-gray-800 dark:border-gray-200 bg-white shadow-lg"></canvas>
|
||||
<div id="result-overlay"
|
||||
class="absolute top-2 left-2 bg-white/90 p-2 rounded shadow text-sm font-mono border hidden"></div>
|
||||
</div>
|
||||
<div id="stats" class="mt-4 text-center text-lg font-semibold min-h-[100px]"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
window.MathJax = {
|
||||
tex: {
|
||||
inlineMath: [['$', '$'], ['\\(', '\\)']]
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const canvas = document.getElementById('pigeonholeCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const statsDiv = document.getElementById('stats');
|
||||
const overlayDiv = document.getElementById('result-overlay');
|
||||
|
||||
// Square dimensions (leaving some padding)
|
||||
const PADDING = 50;
|
||||
const SIDE = 400; // side length
|
||||
|
||||
function drawSquare() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Draw the main square
|
||||
ctx.strokeStyle = '#333';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(PADDING, PADDING, SIDE, SIDE);
|
||||
|
||||
// Draw grid lines to visualize the quadrants (the proof concept)
|
||||
ctx.strokeStyle = '#ccc';
|
||||
ctx.setLineDash([5, 5]);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(PADDING + SIDE / 2, PADDING);
|
||||
ctx.lineTo(PADDING + SIDE / 2, PADDING + SIDE);
|
||||
ctx.moveTo(PADDING, PADDING + SIDE / 2);
|
||||
ctx.lineTo(PADDING + SIDE, PADDING + SIDE / 2);
|
||||
ctx.stroke();
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
|
||||
function generatePoints() {
|
||||
drawSquare();
|
||||
|
||||
const points = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
// Random x, y within the square
|
||||
const x = Math.random() * SIDE + PADDING;
|
||||
const y = Math.random() * SIDE + PADDING;
|
||||
points.push({ x, y, id: i });
|
||||
|
||||
// Draw point
|
||||
ctx.fillStyle = '#ef4444'; // red-500
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, 6, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// Label
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.font = '12px sans-serif';
|
||||
ctx.fillText(i + 1, x + 8, y - 8);
|
||||
}
|
||||
|
||||
findClosestPair(points);
|
||||
}
|
||||
|
||||
function findClosestPair(points) {
|
||||
let minDist = Infinity;
|
||||
let p1 = null;
|
||||
let p2 = null;
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
for (let j = i + 1; j < points.length; j++) {
|
||||
const dx = points[i].x - points[j].x;
|
||||
const dy = points[i].y - points[j].y;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (dist < minDist) {
|
||||
minDist = dist;
|
||||
p1 = points[i];
|
||||
p2 = points[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Highlight closest pair
|
||||
if (p1 && p2) {
|
||||
ctx.strokeStyle = '#2563eb'; // blue-600
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(p1.x, p1.y);
|
||||
ctx.lineTo(p2.x, p2.y);
|
||||
ctx.stroke();
|
||||
|
||||
const limit = SIDE / Math.sqrt(2); // ~282.84 for SIDE=400
|
||||
|
||||
// Generate Coordinate List
|
||||
let pointsHtml = `<div class="mt-4 text-left inline-block"><h3 class="font-bold text-center mb-2">Point Coordinates (relative to square)</h3><ul class="list-none space-y-1">`;
|
||||
points.forEach(p => {
|
||||
const isClosest = (p === p1 || p === p2);
|
||||
// Calculate relative coordinates
|
||||
const relX = (p.x - PADDING).toFixed(1);
|
||||
const relY = (p.y - PADDING).toFixed(1);
|
||||
const style = isClosest ? "color: #2563eb; font-weight: bold;" : "";
|
||||
const mark = isClosest ? "★ " : " ";
|
||||
pointsHtml += `<li style="${style}" class="font-mono text-sm">${mark}Point ${p.id + 1}: (${relX}, ${relY})</li>`;
|
||||
});
|
||||
pointsHtml += `</ul></div>`;
|
||||
|
||||
// Using LaTeX for the dynamic output as well
|
||||
statsDiv.innerHTML = `
|
||||
<div>Closest Pair Distance: $d = ${minDist.toFixed(2)}$ px (between P${p1.id + 1} and P${p2.id + 1})</div>
|
||||
<div>Threshold: $\\frac{s}{\\sqrt{2}} \\approx ${limit.toFixed(2)}$ px</div>
|
||||
<div>Square Side: $s = ${SIDE}$ px</div>
|
||||
${pointsHtml}
|
||||
`;
|
||||
|
||||
if (minDist < SIDE) {
|
||||
statsDiv.innerHTML += `<div class="mt-2 text-green-600 font-bold">✔ $d < s$ (Requested Condition Holds)</div>`;
|
||||
}
|
||||
|
||||
// Trigger MathJax re-render if available
|
||||
if (window.MathJax && window.MathJax.typesetPromise) {
|
||||
window.MathJax.typesetPromise([statsDiv]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initial draw - wait for MathJax to load for the first text,
|
||||
// but we can draw immediately, typesetting stats happens in findClosestPair
|
||||
generatePoints();
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user