added tsp

This commit is contained in:
Rui Ribeiro 2025-09-22 16:53:29 +01:00
commit 2dfffb7ab5
6 changed files with 425 additions and 0 deletions

26
tsp/Makefile Normal file
View File

@ -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

View File

@ -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! 🎉

8
tsp/square.txt Normal file
View File

@ -0,0 +1,8 @@
0, 0
1, 0
2, 0
0, 1
1, 1
2, 1
5, 2
2, 2

64
tsp/subject.txt Normal file
View File

@ -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

66
tsp/tsp.c Normal file
View File

@ -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);
}

14
tsp/tsp.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef TSP_H
# define TSP_H
# include <stdio.h>
# include <stdlib.h>
# include <math.h>
typedef struct s_city
{
float x;
float y;
} t_city;
#endif