Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ A free, interactive web tool to learn algorithms through animated step-by-step v
- **Variable tracking** — see the state of every variable in real time
- **Contextual explanation** — understand the _why_ behind each operation

## 40+ algorithms across 8 categories
## 41+ algorithms across 8 categories

<table>
<tr>
<td width="25%" valign="top">

### Sorting

Bubble Sort · Selection Sort · Insertion Sort · Quick Sort · Merge Sort · Heap Sort · Counting Sort · Radix Sort · Shell Sort
Bubble Sort · Selection Sort · Insertion Sort · Quick Sort · Merge Sort · Heap Sort · Counting Sort · Radix Sort · Shell Sort · Bucket Sort

</td>
<td width="25%" valign="top">
Expand Down Expand Up @@ -79,7 +79,7 @@ N-Queens · Sudoku Solver · Maze Pathfinding

### Divide & Conquer

Tower of Hanoi
Tower of Hanoi · Binary Exponentiation

</td>
<td width="25%" valign="top">
Expand Down
6 changes: 3 additions & 3 deletions README_ES.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ Una herramienta web interactiva y gratuita para aprender algoritmos a través de
- **Seguimiento de variables** — ve el estado de cada variable en tiempo real
- **Explicación contextual** — entiende el _porqué_ de cada operación

## +40 algoritmos en 8 categorías
## +41 algoritmos en 8 categorías

<table>
<tr>
<td width="25%" valign="top">

### Ordenamiento

Bubble Sort · Selection Sort · Insertion Sort · Quick Sort · Merge Sort · Heap Sort · Counting Sort · Radix Sort · Shell Sort
Bubble Sort · Selection Sort · Insertion Sort · Quick Sort · Merge Sort · Heap Sort · Counting Sort · Radix Sort · Shell Sort · Bucket Sort

</td>
<td width="25%" valign="top">
Expand Down Expand Up @@ -79,7 +79,7 @@ N-Queens · Sudoku Solver · Maze Pathfinding

### Divide y vencerás

Torre de Hanói
Torre de Hanói · Exponenciación Binaria

</td>
<td width="25%" valign="top">
Expand Down
58 changes: 58 additions & 0 deletions src/content/algorithms/binary-exponentiation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { Locale } from '@i18n/translations'

const descriptions: Record<Locale, string> = {
en: `Binary Exponentiation

Binary Exponentiation computes aⁿ in O(log n) time by halving the exponent at each step instead of multiplying a by itself n times.

How it works:
1. Base case: a⁰ = 1
2. Recursively compute half = binPow(base, floor(exp / 2))
3. If exp is even, return half × half
4. If exp is odd, return half × half × base

Why it is fast:
Each recursive call cuts the exponent in half, so the recursion depth is proportional to log₂ n instead of n.

Time Complexity:
Best: O(1)
Average: O(log n)
Worst: O(log n)

Space Complexity: O(log n) for the recursive call stack

Applications:
- Modular exponentiation in cryptography
- Fast matrix exponentiation
- Competitive programming and number theory

The key insight is that powers can be reused: once you know a^(n/2), you can square it to recover most of the work immediately.`,
es: `Exponenciación Binaria

La Exponenciación Binaria calcula aⁿ en O(log n) dividiendo el exponente a la mitad en cada paso, en lugar de multiplicar a por sí mismo n veces.

Cómo funciona:
1. Caso base: a⁰ = 1
2. Calcular recursivamente half = binPow(base, floor(exp / 2))
3. Si exp es par, retornar half × half
4. Si exp es impar, retornar half × half × base

Por qué es rápida:
Cada llamada recursiva corta el exponente a la mitad, así que la profundidad de la recursión es proporcional a log₂ n en vez de n.

Complejidad Temporal:
Mejor: O(1)
Promedio: O(log n)
Peor: O(log n)

Complejidad Espacial: O(log n) por la pila de llamadas recursivas

Aplicaciones:
- Exponenciación modular en criptografía
- Exponenciación rápida de matrices
- Programación competitiva y teoría de números

La idea clave es reutilizar potencias: una vez que conoces a^(n/2), puedes elevarlo al cuadrado y recuperar la mayor parte del trabajo inmediatamente.`,
}

export default descriptions
7 changes: 7 additions & 0 deletions src/lib/algorithms/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,13 @@ export const algorithmCatalog: AlgorithmSummary[] = [
difficulty: 'intermediate',
visualization: 'matrix',
},
{
id: 'binary-exponentiation',
name: 'Binary Exponentiation',
category: 'Divide and Conquer',
difficulty: 'intermediate',
visualization: 'concept',
},
// Math
{
id: 'euclidean',
Expand Down
10 changes: 10 additions & 0 deletions src/lib/algorithms/cpp/divide-and-conquer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,14 @@ export const divideAndConquerCpp: Record<string, CodeImplementation> = {
}

hanoi(3, "A", "C", "B"); //@14`),
'binary-exponentiation': annotated(`long long binPow(long long base, long long exp) {
if (exp == 0) return 1; //@2
long long half = binPow(base, exp / 2); //@3
if (exp % 2 == 0) { //@4
return half * half; //@5
}
return half * half * base; //@7
}

binPow(2, 10);`),
}
209 changes: 208 additions & 1 deletion src/lib/algorithms/divide-and-conquer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,211 @@ hanoi(3, 'A', 'C', 'B');`,
},
}

export { towerOfHanoi }
const binaryExponentiation: Algorithm = {
id: 'binary-exponentiation',
name: 'Binary Exponentiation',
category: 'Divide and Conquer',
difficulty: 'intermediate',
visualization: 'concept',
code: `function binPow(base, exp) {
if (exp === 0) return 1
const half = binPow(base, exp >> 1)
if (exp % 2 === 0) {
return half * half
}
return half * half * base
}

binPow(2, 10);`,

generateSteps(locale = 'en') {
const steps: Step[] = []

steps.push({
concept: { type: 'callStack', frames: [] },
description: d(
locale,
"Let's compute 2¹⁰ = 1024 using binary exponentiation. Instead of 9 multiplications, we need only 4 recursive calls.",
'Calculemos 2¹⁰ = 1024 con exponenciación binaria. En lugar de 9 multiplicaciones, solo necesitamos 4 llamadas recursivas.',
),
codeLine: 1,
variables: { base: 2, exp: 10 },
})

steps.push({
concept: {
type: 'callStack',
frames: [
{ label: 'binPow(2, 10)', detail: 'exp=10 is even → call binPow(2, 5)', state: 'active' },
],
},
description: d(
locale,
'binPow(2, 10): divide the exponent by 2 and recurse on 5.',
'binPow(2, 10): dividir el exponente entre 2 y recursar sobre 5.',
),
codeLine: 3,
variables: { base: 2, exp: 10 },
})

steps.push({
concept: {
type: 'callStack',
frames: [
{ label: 'binPow(2, 10)', detail: 'waiting for binPow(2, 5)…', state: 'waiting' },
{ label: 'binPow(2, 5)', detail: 'exp=5 is odd → call binPow(2, 2)', state: 'active' },
],
},
description: d(
locale,
'binPow(2, 5): recurse on 2. Odd exponents will multiply by the base on the way back.',
'binPow(2, 5): recursar sobre 2. Los exponentes impares multiplicarán por la base al regresar.',
),
codeLine: 3,
variables: { base: 2, exp: 5, stackDepth: 2 },
})

steps.push({
concept: {
type: 'callStack',
frames: [
{ label: 'binPow(2, 10)', detail: 'waiting for binPow(2, 5)…', state: 'waiting' },
{ label: 'binPow(2, 5)', detail: 'waiting for binPow(2, 2)…', state: 'waiting' },
{ label: 'binPow(2, 2)', detail: 'exp=2 is even → call binPow(2, 1)', state: 'active' },
],
},
description: d(
locale,
'binPow(2, 2): recurse on 1. The stack depth is growing logarithmically.',
'binPow(2, 2): recursar sobre 1. La profundidad de la pila crece logarítmicamente.',
),
codeLine: 3,
variables: { base: 2, exp: 2, stackDepth: 3 },
})

steps.push({
concept: {
type: 'callStack',
frames: [
{ label: 'binPow(2, 10)', detail: 'waiting for binPow(2, 5)…', state: 'waiting' },
{ label: 'binPow(2, 5)', detail: 'waiting for binPow(2, 2)…', state: 'waiting' },
{ label: 'binPow(2, 2)', detail: 'waiting for binPow(2, 1)…', state: 'waiting' },
{ label: 'binPow(2, 1)', detail: 'exp=1 is odd → call binPow(2, 0)', state: 'active' },
],
},
description: d(
locale,
'binPow(2, 1): recurse on 0, which will trigger the base case.',
'binPow(2, 1): recursar sobre 0, lo que activará el caso base.',
),
codeLine: 3,
variables: { base: 2, exp: 1, stackDepth: 4 },
})

steps.push({
concept: {
type: 'callStack',
frames: [
{ label: 'binPow(2, 10)', detail: 'waiting for binPow(2, 5)…', state: 'waiting' },
{ label: 'binPow(2, 5)', detail: 'waiting for binPow(2, 2)…', state: 'waiting' },
{ label: 'binPow(2, 2)', detail: 'waiting for binPow(2, 1)…', state: 'waiting' },
{ label: 'binPow(2, 1)', detail: 'waiting for binPow(2, 0)…', state: 'waiting' },
{ label: 'binPow(2, 0)', detail: 'BASE CASE: return 1', state: 'base' },
],
},
description: d(
locale,
'Base case: any number to the power of 0 is 1. Now unwind the stack.',
'Caso base: cualquier número elevado a 0 es 1. Ahora desenrollamos la pila.',
),
codeLine: 2,
variables: { base: 2, exp: 0, returns: 1, stackDepth: 4 },
})

steps.push({
concept: {
type: 'callStack',
frames: [
{ label: 'binPow(2, 10)', detail: 'waiting for binPow(2, 5)…', state: 'waiting' },
{ label: 'binPow(2, 5)', detail: 'waiting for binPow(2, 2)…', state: 'waiting' },
{ label: 'binPow(2, 2)', detail: 'waiting for binPow(2, 1)…', state: 'waiting' },
{ label: 'binPow(2, 1)', detail: 'half=1, odd → 1×1×2 = 2', state: 'active' },
],
},
description: d(
locale,
'binPow(2, 1): odd exponent, so multiply by the base after squaring the half result.',
'binPow(2, 1): exponente impar, así que se multiplica por la base después de elevar half al cuadrado.',
),
codeLine: 7,
variables: { base: 2, exp: 1, half: 1, returns: 2 },
})

steps.push({
concept: {
type: 'callStack',
frames: [
{ label: 'binPow(2, 10)', detail: 'waiting for binPow(2, 5)…', state: 'waiting' },
{ label: 'binPow(2, 5)', detail: 'waiting for binPow(2, 2)…', state: 'waiting' },
{ label: 'binPow(2, 2)', detail: 'half=2, even → 2×2 = 4', state: 'active' },
],
},
description: d(
locale,
'binPow(2, 2): even exponent, so just square the half result.',
'binPow(2, 2): exponente par, así que solo se eleva al cuadrado el resultado half.',
),
codeLine: 5,
variables: { base: 2, exp: 2, half: 2, returns: 4 },
})

steps.push({
concept: {
type: 'callStack',
frames: [
{ label: 'binPow(2, 10)', detail: 'waiting for binPow(2, 5)…', state: 'waiting' },
{ label: 'binPow(2, 5)', detail: 'half=4, odd → 4×4×2 = 32', state: 'active' },
],
},
description: d(
locale,
'binPow(2, 5): odd exponent again, so square 4 and multiply by 2.',
'binPow(2, 5): exponente impar otra vez, así que se eleva 4 al cuadrado y se multiplica por 2.',
),
codeLine: 7,
variables: { base: 2, exp: 5, half: 4, returns: 32 },
})

steps.push({
concept: {
type: 'callStack',
frames: [
{ label: 'binPow(2, 10)', detail: 'half=32, even → 32×32 = 1024', state: 'resolved' },
],
},
description: d(
locale,
'binPow(2, 10): final step. Square 32 to get 1024.',
'binPow(2, 10): paso final. Elevar 32 al cuadrado para obtener 1024.',
),
codeLine: 5,
variables: { base: 2, exp: 10, half: 32, returns: 1024 },
consoleOutput: ['1024'],
})

steps.push({
concept: { type: 'callStack', frames: [] },
description: d(
locale,
'2¹⁰ = 1024, computed with logarithmic recursion depth instead of linear repeated multiplication.',
'2¹⁰ = 1024, calculado con profundidad recursiva logarítmica en lugar de multiplicación repetida lineal.',
),
codeLine: 5,
variables: { result: 1024 },
})

return steps
},
}

export { towerOfHanoi, binaryExponentiation }
3 changes: 2 additions & 1 deletion src/lib/algorithms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import { fibonacciDp, knapsack, lcs } from '@lib/algorithms/dynamic-programming'

import { nQueens, sudokuSolver, mazePathfinding } from '@lib/algorithms/backtracking'

import { towerOfHanoi } from '@lib/algorithms/divide-and-conquer'
import { towerOfHanoi, binaryExponentiation } from '@lib/algorithms/divide-and-conquer'

import { euclideanAlgorithm, sieveOfEratosthenes } from '@lib/algorithms/math'

Expand Down Expand Up @@ -115,6 +115,7 @@ export const algorithms: Algorithm[] = [
mazePathfinding,
// Divide and Conquer
towerOfHanoi,
binaryExponentiation,
// Math
euclideanAlgorithm,
sieveOfEratosthenes,
Expand Down
10 changes: 10 additions & 0 deletions src/lib/algorithms/java/divide-and-conquer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,14 @@ export const divideAndConquerJava: Record<string, CodeImplementation> = {
}

hanoi(3, "A", "C", "B"); //@14`),
'binary-exponentiation': annotated(`long binPow(long base, long exp) {
if (exp == 0) return 1; //@2
long half = binPow(base, exp / 2); //@3
if (exp % 2 == 0) { //@4
return half * half; //@5
}
return half * half * base; //@7
}

binPow(2, 10);`),
}
2 changes: 2 additions & 0 deletions src/lib/algorithms/loaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ const ALGORITHM_LOADERS: Record<string, () => Promise<Algorithm>> = {
// Divide and conquer
'tower-of-hanoi': () =>
import('./divide-and-conquer?algorithm=towerOfHanoi').then(readDefaultAlgorithm),
'binary-exponentiation': () =>
import('./divide-and-conquer?algorithm=binaryExponentiation').then(readDefaultAlgorithm),

// Math
euclidean: () => import('./math?algorithm=euclideanAlgorithm').then(readDefaultAlgorithm),
Expand Down
Loading