Skip to content

Commit 14b4a86

Browse files
committed
Add Sieve of Eratosthenes algorithm in new Math category
1 parent 490c64d commit 14b4a86

5 files changed

Lines changed: 257 additions & 0 deletions

File tree

src/components/ComplexityChart.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ type ComplexityKey =
55
| 'O(log n)'
66
| 'O(√n)'
77
| 'O(n)'
8+
| 'O(n log log n)'
89
| 'O(n log n)'
910
| 'O(n²)'
1011
| 'O(2^n)'
@@ -15,6 +16,7 @@ const COMPLEXITY_FNS: Record<ComplexityKey, (n: number) => number> = {
1516
'O(log n)': (n) => Math.log2(Math.max(1, n)),
1617
'O(√n)': (n) => Math.sqrt(n),
1718
'O(n)': (n) => n,
19+
'O(n log log n)': (n) => n * Math.log2(Math.max(2, Math.log2(Math.max(2, n)))),
1820
'O(n log n)': (n) => n * Math.log2(Math.max(1, n)),
1921
'O(n²)': (n) => n * n,
2022
'O(2^n)': (n) => Math.pow(2, n),
@@ -37,6 +39,7 @@ function normalizeToKey(raw: string): ComplexityKey | null {
3739
if (/2\^|k\^/.test(s)) return 'O(2^n)'
3840
if (/n|sqrt/.test(s)) return 'O(√n)'
3941
if (/n²|n\^2|v²/.test(s)) return 'O(n²)'
42+
if (/nloglogn|n\*loglogn/.test(s)) return 'O(n log log n)'
4043
if (/nlogn|n\*logn|\(v\+e\)log|elog|n\^1\.25/.test(s)) return 'O(n log n)'
4144
if (/loglog/.test(s)) return 'O(log n)'
4245
if (/log/.test(s)) return 'O(log n)'

src/components/Sidebar.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,22 @@ const categoryIcons: Record<string, React.ReactNode> = {
139139
/>
140140
</svg>
141141
),
142+
Math: (
143+
<svg
144+
className="w-3.5 h-3.5"
145+
fill="none"
146+
viewBox="0 0 24 24"
147+
stroke="currentColor"
148+
strokeWidth={2}
149+
aria-hidden="true"
150+
>
151+
<path
152+
strokeLinecap="round"
153+
strokeLinejoin="round"
154+
d="M5 5h14M9 5c0 4-1.5 10-3 14M15 5c0 4 1.5 10 3 14"
155+
/>
156+
</svg>
157+
),
142158
}
143159

144160
const categoryColors: Record<string, { icon: string; badge: string; line: string; active: string }> = {
@@ -190,6 +206,12 @@ const categoryColors: Record<string, { icon: string; badge: string; line: string
190206
line: 'border-indigo-500/20',
191207
active: 'border-l-indigo-400',
192208
},
209+
Math: {
210+
icon: 'text-fuchsia-400',
211+
badge: 'bg-fuchsia-500/10 text-fuchsia-400/70',
212+
line: 'border-fuchsia-500/20',
213+
active: 'border-l-fuchsia-400',
214+
},
193215
}
194216

195217
const defaultCategoryColor = {

src/i18n/translations.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export const translations: Record<Locale, Translations> = {
116116
'Dynamic Programming': 'Dynamic Programming',
117117
Backtracking: 'Backtracking',
118118
'Divide and Conquer': 'Divide and Conquer',
119+
Math: 'Math',
119120
},
120121

121122
algorithmDescriptions: {
@@ -993,6 +994,32 @@ Properties:
993994
- Demonstrates the power of recursion
994995
995996
The puzzle was invented by mathematician Édouard Lucas in 1883. Legend says monks in a temple are moving 64 golden disks — completing the puzzle would mark the end of the world (requiring 18,446,744,073,709,551,615 moves).`,
997+
998+
'sieve-of-eratosthenes': `Sieve of Eratosthenes
999+
1000+
The Sieve of Eratosthenes is a classic algorithm for finding all prime numbers up to a limit n. It works by iteratively marking the multiples of each prime, starting from 2.
1001+
1002+
How it works:
1003+
1. Create a boolean array marking 2..n as potentially prime
1004+
2. For each i from 2 up to √n, if i is still marked prime, mark every multiple of i (starting from i²) as composite
1005+
3. Numbers that remain marked after the loop are the primes ≤ n
1006+
1007+
Why start crossing from i²?
1008+
All smaller multiples of i (2i, 3i, …, (i−1)i) have already been crossed by a smaller prime.
1009+
1010+
Time Complexity:
1011+
Best: O(n log log n)
1012+
Average: O(n log log n)
1013+
Worst: O(n log log n)
1014+
1015+
Space Complexity: O(n)
1016+
1017+
Properties:
1018+
- Deterministic, no randomness
1019+
- Cache-friendly when n fits in memory
1020+
- Foundational for number theory and cryptography preprocessing
1021+
1022+
Named after the Greek mathematician Eratosthenes of Cyrene (~276–194 BCE), this sieve remains one of the most efficient ways to find all small primes and is the basis for many factorization preprocessing steps.`,
9961023
},
9971024
},
9981025

@@ -1048,6 +1075,7 @@ The puzzle was invented by mathematician Édouard Lucas in 1883. Legend says mon
10481075
'Dynamic Programming': 'Programación Dinámica',
10491076
Backtracking: 'Backtracking',
10501077
'Divide and Conquer': 'Divide y Vencerás',
1078+
Math: 'Matemáticas',
10511079
},
10521080

10531081
algorithmDescriptions: {
@@ -1925,6 +1953,32 @@ Propiedades:
19251953
- Demuestra el poder de la recursión
19261954
19271955
El rompecabezas fue inventado por el matemático Édouard Lucas en 1883. La leyenda dice que monjes en un templo están moviendo 64 discos dorados — completar el rompecabezas marcaría el fin del mundo (requiriendo 18.446.744.073.709.551.615 movimientos).`,
1956+
1957+
'sieve-of-eratosthenes': `Criba de Eratóstenes
1958+
1959+
La Criba de Eratóstenes es un algoritmo clásico para encontrar todos los números primos hasta un límite n. Funciona marcando iterativamente los múltiplos de cada primo, empezando por 2.
1960+
1961+
Cómo funciona:
1962+
1. Crea un arreglo booleano marcando 2..n como potencialmente primos
1963+
2. Para cada i desde 2 hasta √n, si i sigue marcado como primo, marca todos sus múltiplos (empezando desde i²) como compuestos
1964+
3. Los números que permanezcan marcados al terminar el bucle son los primos ≤ n
1965+
1966+
¿Por qué empezar a tachar desde i²?
1967+
Todos los múltiplos menores de i (2i, 3i, …, (i−1)i) ya fueron tachados por un primo más pequeño.
1968+
1969+
Complejidad Temporal:
1970+
Mejor: O(n log log n)
1971+
Promedio: O(n log log n)
1972+
Peor: O(n log log n)
1973+
1974+
Complejidad Espacial: O(n)
1975+
1976+
Propiedades:
1977+
- Determinista, sin aleatoriedad
1978+
- Eficiente en caché cuando n cabe en memoria
1979+
- Fundamento para teoría de números y preprocesamiento criptográfico
1980+
1981+
Lleva el nombre del matemático griego Eratóstenes de Cirene (~276–194 a.C.). Esta criba sigue siendo una de las formas más eficientes de encontrar todos los primos pequeños y es la base de muchos pasos de preprocesamiento para factorización.`,
19281982
},
19291983
},
19301984
}

src/lib/algorithms/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ import {
6161

6262
import { towerOfHanoi } from '@lib/algorithms/divide-and-conquer'
6363

64+
import { sieveOfEratosthenes } from '@lib/algorithms/math'
65+
6466
export const algorithms: Algorithm[] = [
6567
// Concepts
6668
bigONotation,
@@ -109,6 +111,8 @@ export const algorithms: Algorithm[] = [
109111
mazePathfinding,
110112
// Divide and Conquer
111113
towerOfHanoi,
114+
// Math
115+
sieveOfEratosthenes,
112116
]
113117

114118
export const categories: Category[] = [
@@ -126,4 +130,5 @@ export const categories: Category[] = [
126130
name: 'Divide and Conquer',
127131
algorithms: algorithms.filter((a) => a.category === 'Divide and Conquer'),
128132
},
133+
{ name: 'Math', algorithms: algorithms.filter((a) => a.category === 'Math') },
129134
]

src/lib/algorithms/math.ts

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import type { Algorithm, Step, HighlightType } from '@lib/types'
2+
import { d } from '@lib/algorithms/shared'
3+
4+
const sieveOfEratosthenes: Algorithm = {
5+
id: 'sieve-of-eratosthenes',
6+
name: 'Sieve of Eratosthenes',
7+
category: 'Math',
8+
difficulty: 'intermediate',
9+
visualization: 'matrix',
10+
code: `function sieveOfEratosthenes(n) {
11+
const isPrime = new Array(n + 1).fill(true);
12+
isPrime[0] = isPrime[1] = false;
13+
14+
for (let i = 2; i * i <= n; i++) {
15+
if (isPrime[i]) {
16+
for (let j = i * i; j <= n; j += i) {
17+
isPrime[j] = false;
18+
}
19+
}
20+
}
21+
22+
return isPrime
23+
.map((p, i) => p ? i : null)
24+
.filter(x => x !== null);
25+
}
26+
27+
sieveOfEratosthenes(30);`,
28+
description: `Sieve of Eratosthenes
29+
30+
The Sieve of Eratosthenes is a classic algorithm for finding all prime numbers up to a limit n. It works by iteratively marking the multiples of each prime, starting from 2.
31+
32+
How it works:
33+
1. Create a boolean array marking 2..n as potentially prime
34+
2. For each i from 2 up to √n, if i is still marked prime, mark every multiple of i (starting from i²) as composite
35+
3. Numbers that remain marked after the loop are the primes ≤ n
36+
37+
Why start crossing from i²?
38+
All smaller multiples of i (2i, 3i, …, (i−1)i) have already been crossed by a smaller prime.
39+
40+
Time Complexity:
41+
Best: O(n log log n)
42+
Average: O(n log log n)
43+
Worst: O(n log log n)
44+
45+
Space Complexity: O(n)
46+
47+
Properties:
48+
- Deterministic, no randomness
49+
- Cache-friendly when n fits in memory
50+
- Foundational for number theory and cryptography preprocessing`,
51+
52+
generateSteps(locale = 'en') {
53+
const N = 30
54+
const COLS = 6
55+
const ROWS = Math.ceil(N / COLS)
56+
57+
const values: (number | string)[][] = Array.from({ length: ROWS }, (_, r) =>
58+
Array.from({ length: COLS }, (_, c) => r * COLS + c + 1),
59+
)
60+
61+
const cellOf = (v: number): [number, number] => [Math.floor((v - 1) / COLS), (v - 1) % COLS]
62+
63+
const steps: Step[] = []
64+
const composite = new Set<number>()
65+
66+
const buildHighlights = (
67+
currentPrime: number | null,
68+
currentMultiple: number | null,
69+
): Record<string, HighlightType> => {
70+
const h: Record<string, HighlightType> = {}
71+
h['0,0'] = 'sorted'
72+
for (const c of composite) {
73+
const [r, col] = cellOf(c)
74+
h[`${r},${col}`] = 'placed'
75+
}
76+
if (currentPrime != null) {
77+
const [r, col] = cellOf(currentPrime)
78+
h[`${r},${col}`] = 'current'
79+
}
80+
if (currentMultiple != null) {
81+
const [r, col] = cellOf(currentMultiple)
82+
h[`${r},${col}`] = 'checking'
83+
}
84+
return h
85+
}
86+
87+
steps.push({
88+
matrix: {
89+
rows: ROWS,
90+
cols: COLS,
91+
values,
92+
highlights: buildHighlights(null, null),
93+
},
94+
description: d(
95+
locale,
96+
`Initialize: assume every number from 2 to ${N} is prime. 1 is excluded by definition.`,
97+
`Inicializar: asumimos que todo número de 2 a ${N} es primo. 1 se excluye por definición.`,
98+
),
99+
codeLine: 2,
100+
variables: { n: N, primes: 0 },
101+
})
102+
103+
const limit = Math.floor(Math.sqrt(N))
104+
for (let i = 2; i <= limit; i++) {
105+
if (composite.has(i)) continue
106+
107+
steps.push({
108+
matrix: {
109+
rows: ROWS,
110+
cols: COLS,
111+
values,
112+
highlights: buildHighlights(i, null),
113+
},
114+
description: d(
115+
locale,
116+
`${i} is still marked prime. Cross out its multiples starting from ${i}² = ${i * i}.`,
117+
`${i} sigue marcado como primo. Tachar sus múltiplos empezando en ${i}² = ${i * i}.`,
118+
),
119+
codeLine: 5,
120+
variables: { i, 'i*i': i * i },
121+
})
122+
123+
for (let j = i * i; j <= N; j += i) {
124+
steps.push({
125+
matrix: {
126+
rows: ROWS,
127+
cols: COLS,
128+
values,
129+
highlights: buildHighlights(i, j),
130+
},
131+
description: d(
132+
locale,
133+
`Mark ${j} as composite (multiple of ${i}).`,
134+
`Marcar ${j} como compuesto (múltiplo de ${i}).`,
135+
),
136+
codeLine: 7,
137+
variables: { i, j },
138+
})
139+
composite.add(j)
140+
}
141+
}
142+
143+
const primes: number[] = []
144+
for (let k = 2; k <= N; k++) if (!composite.has(k)) primes.push(k)
145+
146+
const finalHighlights: Record<string, HighlightType> = { '0,0': 'sorted' }
147+
for (let k = 2; k <= N; k++) {
148+
const [r, col] = cellOf(k)
149+
finalHighlights[`${r},${col}`] = composite.has(k) ? 'placed' : 'pivot'
150+
}
151+
152+
steps.push({
153+
matrix: {
154+
rows: ROWS,
155+
cols: COLS,
156+
values,
157+
highlights: finalHighlights,
158+
},
159+
description: d(
160+
locale,
161+
`Done. Primes ≤ ${N}: ${primes.join(', ')} (${primes.length} primes).`,
162+
`Listo. Primos ≤ ${N}: ${primes.join(', ')} (${primes.length} primos).`,
163+
),
164+
codeLine: 12,
165+
variables: { count: primes.length, primes: primes.join(',') },
166+
consoleOutput: [`[${primes.join(', ')}]`],
167+
})
168+
169+
return steps
170+
},
171+
}
172+
173+
export { sieveOfEratosthenes }

0 commit comments

Comments
 (0)