update pigeonhole
This commit is contained in:
Binary file not shown.
@@ -9,4 +9,10 @@ templates = Jinja2Templates(directory="templates")
|
|||||||
async def browser_local_cryptography_strength(request: Request):
|
async def browser_local_cryptography_strength(request: Request):
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request=request, name="browser_random_number.html", contents={"rand": "rand"}
|
request=request, name="browser_random_number.html", contents={"rand": "rand"}
|
||||||
|
)
|
||||||
|
|
||||||
|
@security_router.get("/pigeonhole")
|
||||||
|
async def pigeonhole_principle_simulation(request: Request):
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
request=request, name="pigeonhole.html"
|
||||||
)
|
)
|
||||||
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