@@ -35,7 +35,7 @@ A free, interactive web tool to learn algorithms through animated step-by-step v
### 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
|
@@ -79,7 +79,7 @@ N-Queens · Sudoku Solver · Maze Pathfinding
### Divide & Conquer
-Tower of Hanoi
+Tower of Hanoi · Binary Exponentiation
|
diff --git a/README_ES.md b/README_ES.md
index c611006..7a21395 100644
--- a/README_ES.md
+++ b/README_ES.md
@@ -27,7 +27,7 @@ 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
@@ -35,7 +35,7 @@ Una herramienta web interactiva y gratuita para aprender algoritmos a través de
### 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
|
@@ -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
|
diff --git a/src/content/algorithms/binary-exponentiation.ts b/src/content/algorithms/binary-exponentiation.ts
new file mode 100644
index 0000000..243dd75
--- /dev/null
+++ b/src/content/algorithms/binary-exponentiation.ts
@@ -0,0 +1,58 @@
+import type { Locale } from '@i18n/translations'
+
+const descriptions: Record = {
+ 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
diff --git a/src/lib/algorithms/catalog.ts b/src/lib/algorithms/catalog.ts
index 419dc99..1bef987 100644
--- a/src/lib/algorithms/catalog.ts
+++ b/src/lib/algorithms/catalog.ts
@@ -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',
diff --git a/src/lib/algorithms/cpp/divide-and-conquer.ts b/src/lib/algorithms/cpp/divide-and-conquer.ts
index 8dca7b5..bd81069 100644
--- a/src/lib/algorithms/cpp/divide-and-conquer.ts
+++ b/src/lib/algorithms/cpp/divide-and-conquer.ts
@@ -17,4 +17,14 @@ export const divideAndConquerCpp: Record = {
}
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);`),
}
diff --git a/src/lib/algorithms/divide-and-conquer.ts b/src/lib/algorithms/divide-and-conquer.ts
index 031f0b8..fbcc9e9 100644
--- a/src/lib/algorithms/divide-and-conquer.ts
+++ b/src/lib/algorithms/divide-and-conquer.ts
@@ -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 }
diff --git a/src/lib/algorithms/index.ts b/src/lib/algorithms/index.ts
index c646800..c00c0a4 100644
--- a/src/lib/algorithms/index.ts
+++ b/src/lib/algorithms/index.ts
@@ -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'
@@ -115,6 +115,7 @@ export const algorithms: Algorithm[] = [
mazePathfinding,
// Divide and Conquer
towerOfHanoi,
+ binaryExponentiation,
// Math
euclideanAlgorithm,
sieveOfEratosthenes,
diff --git a/src/lib/algorithms/java/divide-and-conquer.ts b/src/lib/algorithms/java/divide-and-conquer.ts
index 0c2d32c..c120c54 100644
--- a/src/lib/algorithms/java/divide-and-conquer.ts
+++ b/src/lib/algorithms/java/divide-and-conquer.ts
@@ -17,4 +17,14 @@ export const divideAndConquerJava: Record = {
}
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);`),
}
diff --git a/src/lib/algorithms/loaders.ts b/src/lib/algorithms/loaders.ts
index 71f2027..8438f1b 100644
--- a/src/lib/algorithms/loaders.ts
+++ b/src/lib/algorithms/loaders.ts
@@ -95,6 +95,8 @@ const ALGORITHM_LOADERS: Record Promise> = {
// 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),
diff --git a/src/lib/algorithms/python/divide-and-conquer.ts b/src/lib/algorithms/python/divide-and-conquer.ts
index d3c7bd0..7fc25dc 100644
--- a/src/lib/algorithms/python/divide-and-conquer.ts
+++ b/src/lib/algorithms/python/divide-and-conquer.ts
@@ -17,4 +17,15 @@ export const divideAndConquerPython: Record = {
hanoi(3, "A", "C", "B") #@14`),
+ 'binary-exponentiation': annotated(`def bin_pow(base, exp): #@1
+ if exp == 0:
+ return 1 #@2
+
+ half = bin_pow(base, exp // 2) #@3
+ if exp % 2 == 0: #@4
+ return half * half #@5
+ return half * half * base #@7
+
+
+bin_pow(2, 10)`),
}
diff --git a/src/lib/algorithms/rust/divide-and-conquer.ts b/src/lib/algorithms/rust/divide-and-conquer.ts
index 74fa969..a2795fd 100644
--- a/src/lib/algorithms/rust/divide-and-conquer.ts
+++ b/src/lib/algorithms/rust/divide-and-conquer.ts
@@ -18,4 +18,17 @@ export const divideAndConquerRust: Record = {
}
hanoi(3, "A", "C", "B"); //@14`),
+ 'binary-exponentiation': annotated(`fn bin_pow(base: i64, exp: i64) -> i64 {
+ if exp == 0 {
+ return 1; //@2
+ }
+
+ let half = bin_pow(base, exp / 2); //@3
+ if exp % 2 == 0 { //@4
+ return half * half; //@5
+ }
+ half * half * base //@7
+}
+
+bin_pow(2, 10);`),
}
| |