From 2dfffb7ab5f5588cde32764089c4f89cdec8a055 Mon Sep 17 00:00:00 2001 From: Rui Ribeiro <42305006+ruiribeiro04@users.noreply.github.com> Date: Mon, 22 Sep 2025 16:53:29 +0100 Subject: [PATCH] added tsp --- tsp/Makefile | 26 ++++ tsp/TSP_Algorithm_Explained.md | 247 +++++++++++++++++++++++++++++++++ tsp/square.txt | 8 ++ tsp/subject.txt | 64 +++++++++ tsp/tsp.c | 66 +++++++++ tsp/tsp.h | 14 ++ 6 files changed, 425 insertions(+) create mode 100644 tsp/Makefile create mode 100644 tsp/TSP_Algorithm_Explained.md create mode 100644 tsp/square.txt create mode 100644 tsp/subject.txt create mode 100644 tsp/tsp.c create mode 100644 tsp/tsp.h diff --git a/tsp/Makefile b/tsp/Makefile new file mode 100644 index 0000000..dc5ca14 --- /dev/null +++ b/tsp/Makefile @@ -0,0 +1,26 @@ +NAME = tsp + +SRCS = tsp.c +OBJS = $(SRCS:.c=.o) + +CC = gcc +CFLAGS = -Wall -Wextra -Werror +MATH_FLAGS = -lm + +all: $(NAME) + +$(NAME): $(OBJS) + $(CC) $(CFLAGS) $(OBJS) -o $(NAME) $(MATH_FLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(OBJS) + +fclean: clean + rm -f $(NAME) + +re: fclean all + +.PHONY: all clean fclean re \ No newline at end of file diff --git a/tsp/TSP_Algorithm_Explained.md b/tsp/TSP_Algorithm_Explained.md new file mode 100644 index 0000000..42531ed --- /dev/null +++ b/tsp/TSP_Algorithm_Explained.md @@ -0,0 +1,247 @@ +# The Traveling Salesman Problem (TSP) - Simple Explanation + +## What is the Traveling Salesman Problem? πŸ—ΊοΈ + +Imagine you're a delivery person who needs to visit several houses in your neighborhood and then return home. You want to find the **shortest possible route** that visits every house exactly once and brings you back to where you started. + +That's exactly what the Traveling Salesman Problem is about! + +### Real-World Example +Let's say you need to deliver pizza to 4 houses: +- Your home: (0, 0) +- House A: (1, 0) +- House B: (1, 1) +- House C: (0, 1) + +``` +C ---- B +| | +| | +Home - A +``` + +You could go in many different orders: +- Home β†’ A β†’ B β†’ C β†’ Home +- Home β†’ A β†’ C β†’ B β†’ Home +- Home β†’ B β†’ A β†’ C β†’ Home +- ... and many more! + +But which path is the shortest? That's what our algorithm finds out! + +## How Our Algorithm Works πŸ” + +Our solution uses a "brute force" approach, which means: +**"Let's try EVERY possible path and pick the shortest one!"** + +It's like trying on every shirt in your closet to find the one that fits best - not the fastest way, but it guarantees you'll find the perfect answer! + +### Step 1: Calculate Distance Between Two Points + +First, we need to know how far apart two places are: + +```c +static float distance(t_city a, t_city b) +{ + float dx = b.x - a.x; // How far left/right? + float dy = b.y - a.y; // How far up/down? + return (sqrtf(dx * dx + dy * dy)); // Pythagorean theorem! +} +``` + +**What's happening here?** +- `dx` = horizontal distance (like counting steps left or right) +- `dy` = vertical distance (like counting steps up or down) +- `sqrtf(dx*dx + dy*dy)` = the straight-line distance (like a bird flying) + +**Example:** +From Home (0,0) to House A (1,0): +- dx = 1 - 0 = 1 +- dy = 0 - 0 = 0 +- distance = √(1Β² + 0Β²) = √1 = 1.0 + +### Step 2: Calculate Total Path Length + +Now we add up all the distances in a complete round trip: + +```c +static float path_length(t_city *cities, int *route, int n) +{ + float total = 0; + int i = 0; + + while (i < n) + { + // Add distance from current city to next city + total += distance(cities[route[i]], cities[route[(i + 1) % n]]); + i++; + } + return (total); +} +``` + +**What's that weird `% n` thing?** +It's like a clock! When you get to hour 12, the next hour is 1, not 13. +- When `i = 3` (last city) and `n = 4` (total cities) +- `(i + 1) % n = (3 + 1) % 4 = 4 % 4 = 0` +- So we go from the last city back to the first city (completing the circle!) + +### Step 3: Try All Possible Routes + +This is where the magic happens! We generate every possible order of visiting the cities: + +```c +static void solve(t_city *cities, int *route, int pos, int n, float *best) +{ + float current; + int i, temp; + + // If we've arranged all cities, check this route + if (pos == n) + { + current = path_length(cities, route, n); + if (current < *best) + *best = current; // Found a better route! + return; + } + + // Try putting each remaining city in the current position + i = pos; + while (i < n) + { + // Swap cities (like switching the order of your visits) + temp = route[pos]; + route[pos] = route[i]; + route[i] = temp; + + // Try all arrangements with this city in this position + solve(cities, route, pos + 1, n, best); + + // Swap back (undo the change) + temp = route[pos]; + route[pos] = route[i]; + route[i] = temp; + i++; + } +} +``` + +**Think of it like this:** +- Position 0: "Which city should I visit first?" +- Position 1: "Which city should I visit second?" +- Position 2: "Which city should I visit third?" +- And so on... + +For each position, we try putting each remaining city there and see what happens! + +## Complete Example Walkthrough πŸšΆβ€β™‚οΈ + +Let's trace through a simple 3-city example: +- City 0: (0, 0) +- City 1: (1, 0) +- City 2: (0, 1) + +``` +2 +| +| +0 --- 1 +``` + +**All possible routes:** +1. 0 β†’ 1 β†’ 2 β†’ 0: distance = 1 + √2 + 1 = 3.41 +2. 0 β†’ 2 β†’ 1 β†’ 0: distance = 1 + √2 + 1 = 3.41 +3. 1 β†’ 0 β†’ 2 β†’ 1: distance = 1 + 1 + √2 = 3.41 +4. 1 β†’ 2 β†’ 0 β†’ 1: distance = √2 + 1 + 1 = 3.41 +5. 2 β†’ 0 β†’ 1 β†’ 2: distance = 1 + 1 + √2 = 3.41 +6. 2 β†’ 1 β†’ 0 β†’ 2: distance = √2 + 1 + 1 = 3.41 + +**Result:** All routes have the same length! (This makes sense - it's a triangle, so any direction around it is the same distance.) + +## The Main Program 🏠 + +```c +int main(void) +{ + t_city cities[11]; // Space for up to 11 cities + int route[11]; // The order we'll visit cities + int n = 0; // How many cities we actually have + float best = 999999.0f; // Start with a really big number + + // Read city coordinates from input + while (n < 11 && fscanf(stdin, "%f, %f", &cities[n].x, &cities[n].y) == 2) + { + route[n] = n; // Initially: visit cities in order 0,1,2,3... + n++; + } + + if (n < 2) + return (1); // Need at least 2 cities for a trip! + + solve(cities, route, 0, n, &best); // Find the best route + fprintf(stdout, "%.2f\n", best); // Print the shortest distance + return (0); +} +``` + +## Why This Works (But Is Slow) ⏰ + +**The Good:** +- βœ… **Always finds the perfect answer** - we check every possibility! +- βœ… **Simple to understand** - no complex tricks or shortcuts +- βœ… **Works for any arrangement of cities** + +**The Not-So-Good:** +- ❌ **Gets very slow with more cities** +- For 3 cities: 6 routes to check +- For 4 cities: 24 routes to check +- For 10 cities: 3,628,800 routes to check! 😱 + +**Why it gets so big:** +- 1st city: 10 choices +- 2nd city: 9 choices (can't repeat) +- 3rd city: 8 choices +- ... +- Total: 10 Γ— 9 Γ— 8 Γ— 7 Γ— 6 Γ— 5 Γ— 4 Γ— 3 Γ— 2 Γ— 1 = 3,628,800 + +This is called "factorial growth" - it explodes very quickly! + +## Key Programming Concepts Used πŸ’» + +### 1. **Recursion** +The `solve()` function calls itself! It's like Russian nesting dolls - each call handles one position, then asks a smaller version of itself to handle the rest. + +### 2. **Backtracking** +We try something (swap cities), explore all possibilities, then undo it (swap back). Like trying different paths in a maze and backing up when we hit a dead end. + +### 3. **Permutations** +We generate every possible ordering of the cities. It's like having cards numbered 1,2,3 and arranging them in every possible order: 123, 132, 213, 231, 312, 321. + +### 4. **Global Optimization** +We keep track of the best solution found so far and update it whenever we find something better. + +## Fun Facts! 🎯 + +1. **This problem is NP-hard** - that's computer science speak for "really, really hard to solve quickly" + +2. **Real delivery companies** use approximate algorithms that find "pretty good" solutions much faster than perfect ones + +3. **With 11 cities** (the maximum our program handles), there are 39,916,800 possible routes! + +4. **The problem has applications** in: + - GPS navigation systems + - Circuit board manufacturing + - DNA sequencing + - Planning efficient tours for bands or sports teams + +## Summary πŸ“ + +Our TSP algorithm is like a very thorough friend who insists on checking every possible pizza delivery route before deciding which one is shortest. It's not the fastest approach, but it guarantees the perfect answer! + +The main steps are: +1. **Read** all the city locations +2. **Generate** every possible visiting order +3. **Calculate** the total distance for each order +4. **Remember** the shortest distance found +5. **Print** the answer + +Even though it's not the most efficient algorithm for large problems, it's perfect for learning because it's easy to understand and always gives the correct answer! πŸŽ‰ \ No newline at end of file diff --git a/tsp/square.txt b/tsp/square.txt new file mode 100644 index 0000000..1673c5b --- /dev/null +++ b/tsp/square.txt @@ -0,0 +1,8 @@ +0, 0 +1, 0 +2, 0 +0, 1 +1, 1 +2, 1 +5, 2 +2, 2 \ No newline at end of file diff --git a/tsp/subject.txt b/tsp/subject.txt new file mode 100644 index 0000000..afc2bf0 --- /dev/null +++ b/tsp/subject.txt @@ -0,0 +1,64 @@ +Assignment name: tsp +Expected files: *.c *.h +Allowed functions: write, sqrtf, getline, fseek, fscanf, ferror, feof, +fabsf, memcpy, fprintf, fclose, malloc, calloc, realloc, free, fopen, +errno, stderr, stdin, stdout +------------------------------------------------------- + +The first publication referring to this problem as the "traveling salesman +problem" is found in the 1949 RAND Corporation report by Julia Robinson, +"On the Hamiltonian game (a traveling salesman problem)." + +Here is how she defines the problem: + +"The purpose of this note is to give a method for solving a problem related +to the traveling salesman problem. It seems worthwhile to give a description +of the original problem. One formulation is to find the shortest route for a +salesman starting from Washington, visiting all the state capitals and then +returning to Washington. + +More generally, to find the shortest CLOSED CURVE containing n given points +in the plane." + +for example with the following set of cities: +0, 0 +1, 0 +2, 0 +0, 1 +1, 1 +2, 1 +1, 2 +2, 2 +which can be presented as follows: ++ + + ++ + + + + + +the shortest path is: + _____ +|__ | + |__| + +so you should print the length of this path that is: +8.00 + +Write a program that will read a set of city coordinates in the form +'%f, %f\n' from the standard input and will print the length of the shortest +possible path containing all these cities under the form '%.2f'. + +Your program will not be tested with more than 11 cities. + +You will find in this directory a file tsp.c containing all the boring parts of +this exercise and example files to help you test your program. + +hint: in order to use sqrtf, add -lm at the end of your compilation command. + +For example this should work: +$> cat square.txt +1, 1 +0, 1 +1, 0 +0, 0 +$> ./tsp < square.txt | cat -e +4.00$ + +Hint : in order to use sqrtf , add -lm at the end of your compilation command \ No newline at end of file diff --git a/tsp/tsp.c b/tsp/tsp.c new file mode 100644 index 0000000..7bb869f --- /dev/null +++ b/tsp/tsp.c @@ -0,0 +1,66 @@ +#include "tsp.h" + +static float distance(t_city a, t_city b) +{ + float dx = b.x - a.x; + float dy = b.y - a.y; + return (sqrtf(dx * dx + dy * dy)); +} + +static float path_length(t_city *cities, int *route, int n) +{ + float total = 0; + int i = 0; + + while (i < n) + { + total += distance(cities[route[i]], cities[route[(i + 1) % n]]); + i++; + } + return (total); +} + +static void solve(t_city *cities, int *route, int pos, int n, float *best) +{ + float current; + int i, temp; + + if (pos == n) + { + current = path_length(cities, route, n); + if (current < *best) + *best = current; + return; + } + i = pos; + while (i < n) + { + temp = route[pos]; + route[pos] = route[i]; + route[i] = temp; + solve(cities, route, pos + 1, n, best); + temp = route[pos]; + route[pos] = route[i]; + route[i] = temp; + i++; + } +} + +int main(void) +{ + t_city cities[11]; + int route[11]; + int n = 0; + float best = 999999.0f; + + while (n < 11 && fscanf(stdin, "%f, %f", &cities[n].x, &cities[n].y) == 2) + { + route[n] = n; + n++; + } + if (n < 2) + return (1); + solve(cities, route, 0, n, &best); + fprintf(stdout, "%.2f\n", best); + return (0); +} \ No newline at end of file diff --git a/tsp/tsp.h b/tsp/tsp.h new file mode 100644 index 0000000..8d0091d --- /dev/null +++ b/tsp/tsp.h @@ -0,0 +1,14 @@ +#ifndef TSP_H +# define TSP_H + +# include +# include +# include + +typedef struct s_city +{ + float x; + float y; +} t_city; + +#endif \ No newline at end of file