added tsp
This commit is contained in:
commit
2dfffb7ab5
26
tsp/Makefile
Normal file
26
tsp/Makefile
Normal 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
|
||||||
247
tsp/TSP_Algorithm_Explained.md
Normal file
247
tsp/TSP_Algorithm_Explained.md
Normal 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
8
tsp/square.txt
Normal 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
64
tsp/subject.txt
Normal 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
66
tsp/tsp.c
Normal 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);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user