diff --git a/greedy-algorithms/notes.md b/greedy-algorithms/notes.md index 34ac568bd127ac3a3e7bb1e39af72fffa2cd02f9..35d62aceb93583ab247f7e4d5e1e090142657daf 100644 --- a/greedy-algorithms/notes.md +++ b/greedy-algorithms/notes.md @@ -64,7 +64,7 @@ Problem: Given a sequence of fueling sites, the distances between consecutive si * Vertices (situations): * Locations * Edge weights (optional): - * Distance to the next location + * Distance to the next location (optional) * Topological order: * Order along the route: `a → b → … → i` * Goal: @@ -126,8 +126,8 @@ Problem: [same as above] ## Choosing a Forward Edge * Exhaustive search: - * Generate … - * Check that … + * Generate all edges that travel up to distance `range` along the route (generate these in forward order in order to keep a running distance total) + * Check that the chosen edge travels farthest (but, that condition always favors the current candidate) ## Example @@ -143,12 +143,15 @@ Problem: [same as above] Location Next Location -------- ------------- - a … - … + a c + c d + d e + e h + h i Path ---- - a → … + a → c → d → e → h → i -------------------------------------------------------------------------------- @@ -214,22 +217,22 @@ Problem: Given frequencies for a set of symbols, create a lossless binary encodi ## DAG * Edges (actions): - * … + * Combine two parts (sets of symbols) and record bits from what combination we did * Vertices (situations): - * … + * Partitions of the symbols * Edge weights: - * … + * Total frequency of the combined set * Topological order: - * … + * By decreasing number of parts * Goal: - * … + * Find the shortest path from having all sets separated to having all sets combined ## Choosing a Forward Edge * Direct solution: - * … - * … - * … + * Choose the lowest-frequency part + * Choose the second-lowest-frequency part + * Follow the edge that combines those two parts ## Example @@ -241,4 +244,6 @@ Problem: Given frequencies for a set of symbols, create a lossless binary encodi Symbol Partitioning (Frequency Order) 'a' 'b' 'c' 'd' ------------------------------------------------------- --- --- --- --- {'a'} → 0.15, {'c'} → 0.25, {'b'} → 0.30, {'d'} → 0.30 - … + {'a', 'c'} → 0.40, {'b'} → 0.30, {'d'} → 0.30 0 1 + {'a', 'c'} → 0.40, {'b', 'd'} → 0.6 0 0 1 1 + {'a', 'c', 'b', 'd'} → 1.0 00 10 01 11 diff --git a/greedy-algorithms/src/features/encoding/solver.js b/greedy-algorithms/src/features/encoding/solver.js index 7c7344bb7c83a81a24a9cc163affa00b9802e719..0b84bd6b0b7ad91b8d517f3e2f005b6e199a8d6e 100644 --- a/greedy-algorithms/src/features/encoding/solver.js +++ b/greedy-algorithms/src/features/encoding/solver.js @@ -1,8 +1,25 @@ +import { PriorityQueue } from './collections.js'; + export function findEncoding(frequencies) { const results = new Map(); + const partitioning = new PriorityQueue(); for (const [meaning, frequency] of frequencies) { results.set(meaning, ''); + partitioning.insert(new Set([meaning]), frequency); + } + while (partitioning.size > 1) { + const [firstMeanings, firstFrequency] = partitioning.remove(); + const [secondMeanings, secondFrequency] = partitioning.remove(); + for (const meaning of firstMeanings) { + results.set(meaning, `0${results.get(meaning)}`); + } + for (const meaning of secondMeanings) { + results.set(meaning, `1${results.get(meaning)}`); + } + partitioning.insert( + new Set([...firstMeanings, ...secondMeanings]), + firstFrequency + secondFrequency, + ); } - // TODO: stub return results; } diff --git a/greedy-algorithms/src/features/fueling/solver.js b/greedy-algorithms/src/features/fueling/solver.js index 18326349a6727b0110a14ffee9f92a0950e54315..b97bec004a4201343edfd53cf9000fc8b480be30 100644 --- a/greedy-algorithms/src/features/fueling/solver.js +++ b/greedy-algorithms/src/features/fueling/solver.js @@ -7,7 +7,16 @@ export function planFuelings(gaps, range) { gaps.every((gap) => gap <= range), `Tried to find a fueling plan crossing a gap longer than the range ${range}.`, ); - const results = []; - // TODO: stub + const results = [0]; + const destination = gaps.length; + while (results[results.length - 1] < destination) { + let best = undefined; + for (let distance = 0, candidate = results[results.length - 1]; + distance <= range && candidate <= destination; + distance += gaps[candidate], ++candidate) { + best = candidate; + } + results.push(best); + } return results; }