first commit
158
Algo/Louage/Louage.ipynb
Normal file
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Mobility and Smart Cities\n",
|
||||
"\n",
|
||||
"## GILA - Greedy Incremental Louage’s Algorithm\n",
|
||||
"\n",
|
||||
"### Presentation\n",
|
||||
"\n",
|
||||
"Louage is a transportation system in Tunisia to overcome the lack of public transportation. This system is spread across all the Tunisian territory and is used by a lot of people. In this system their are two types of trips : regional and cross-regional.\n",
|
||||
"\n",
|
||||
"This transportation system is very cheap and have a very high occupation level (97%). \n",
|
||||
"\n",
|
||||
"Drivers propose their itineraries and stops with a time window of departure and arrival and a price per seat. \n",
|
||||
"\n",
|
||||
"Passengers can ask to book a trip from a location to a destination with a desire departure and an arrival time window.\n",
|
||||
"\n",
|
||||
"Operators assigned passengers to a trip if the trip is compatible with their time window and if there is enough seats available. First come first served (FIFO). \n",
|
||||
"\n",
|
||||
"Other criteria could also be used like pricing, number of stops, number of changes require to achieve the trip etc. \n",
|
||||
"\n",
|
||||
"### Problem\n",
|
||||
"\n",
|
||||
"The Louage probleme is a multi-constraints and multi-objectives problem.\n",
|
||||
"\n",
|
||||
"Objectives :\n",
|
||||
"- Maximize the number of passengers per vehicle (High occupation rate) to maximize the profit of the driver\n",
|
||||
"- Maximize the number of satisfied requests (e.g. maximize the number of clients)\n",
|
||||
"- Respect first in first out (FIFO) rule\n",
|
||||
"- Minimize the waiting time of the passengers\n",
|
||||
"- Minimize the overall sum of running itineraries duration\n",
|
||||
"- As soon as a louage is full, it should leave\n",
|
||||
"\n",
|
||||
"Constraints :\n",
|
||||
"- Coherence of time windows and travel time with normalization\n",
|
||||
"- Louage’ s lines constraints\n",
|
||||
"- Itinerary constraints\n",
|
||||
"- Stop position depencies respected between request and organization (set of journeys)\n",
|
||||
"- Respect the order of status and conditions on state transition for each journey\n",
|
||||
"- Capacity of the vehicle\n",
|
||||
"- Price\n",
|
||||
"\n",
|
||||
"How can we assigned travelers to their corresponding while respecting the constraints and maximizing the objectives ?\n",
|
||||
"\n",
|
||||
"### Conception\n",
|
||||
"\n",
|
||||
"To solve this problem the GILA algorithm uses a greedy approach. We makes the best decision at each step to reach a local optimum.\n",
|
||||
"\n",
|
||||
"#### Inputs\n",
|
||||
"\n",
|
||||
"We have two types of input in different lists : demands and offers.\n",
|
||||
"\n",
|
||||
"##### Demands\n",
|
||||
"\n",
|
||||
"Each client create a demand with the following information :\n",
|
||||
"- Itinerary / Path (origin, destination)\n",
|
||||
" - Contains all the stops of the itinerary\n",
|
||||
" - Time window of departure (hd_minus, hd_plus)\n",
|
||||
" - Time window of arrival (ha_minus, ha_plus)\n",
|
||||
"- Booked placed (NP)\n",
|
||||
"- Bill\n",
|
||||
"- State (initialized, validated, paired, contractualized, running, realized, non realized and archived), this is used to keep track of the state of the demand during the algorithm.\n",
|
||||
"\n",
|
||||
"##### Offers\n",
|
||||
"\n",
|
||||
"Each driver realized a trip with the following information :\n",
|
||||
"- Itinerary / Path (origin, destination)\n",
|
||||
" - Contains all the stops of the itinerary\n",
|
||||
" - Time window of departure (hd_minus, hd_plus)\n",
|
||||
" - Time window of arrivals (ha_minus, ha_plus)\n",
|
||||
"- Number of available seats (PlacesL)\n",
|
||||
"- The driver\n",
|
||||
"- The vehicle\n",
|
||||
"- Status\n",
|
||||
"\n",
|
||||
"#### Output\n",
|
||||
"\n",
|
||||
"As a result, the algorithm returns an organization of journeys composed of associated the demands and offers.\n",
|
||||
"Each journey is composed of :\n",
|
||||
"- an itinerary\n",
|
||||
"- an offer\n",
|
||||
"- a list of demands\n",
|
||||
"- a state\n",
|
||||
"- a maximal number of seats occupied by passengers at any step of the itinerary\n",
|
||||
"- the number of available seats at any step of the itinerary \n",
|
||||
"\n",
|
||||
"#### Normalization\n",
|
||||
"\n",
|
||||
"When clients are affected to a trip, their time window of departure and arrival are updated to take into account the traveling time and the time window of departure and arrival of the louage.\n",
|
||||
"\n",
|
||||
"We have an offer leaving between 8h and 8h30 for a trip that takes 30min and arriving between 8h to 9h30.\n",
|
||||
"\n",
|
||||
"Normalization will shift the arrival time window of the offer to 8h30 and 9h30.\n",
|
||||
"\n",
|
||||
"Trips must also be coherent:\n",
|
||||
"- each status of journey must be in the defined order\n",
|
||||
"- it's impossible to drop someone before picking him up so pickup location have to be before dropoff location in the itinerary \n",
|
||||
"\n",
|
||||
"### Examples\n",
|
||||
"\n",
|
||||
"#### Status\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#### Pairing \n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#### Normalization\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Implementation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Analysis\n",
|
||||
"\n",
|
||||
"This solution allows to solve the problem in a reasonable time even it does not reach a perfect solution all the time.\n",
|
||||
"\n",
|
||||
"This algorithm can also not fit real life situations. For instance, if a vehicle has 6 passengers seat, that does not mean that you can transport only 6 persons. More person could be fitted inside regardless of the number of seats and regulations."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
},
|
||||
"orig_nbformat": 4
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
BIN
Algo/Louage/Louage_GuillaumeMARTIN_description_v1.zip
Normal file
108
Algo/Louage/louage.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Mobility and Smart Cities
|
||||
|
||||
## GILA - Greedy Incremental Louage’s Algorithm
|
||||
|
||||
### Presentation
|
||||
|
||||
Louage is a transportation system in Tunisia to overcome the lack of public transportation. This system is spread across all the Tunisian territory and is used by a lot of people. In this system their are two types of trips : regional and cross-regional.
|
||||
|
||||
This transportation system is very cheap and have a very high occupation level (97%).
|
||||
|
||||
Drivers propose their itineraries and stops with a time window of departure and arrival and a price per seat.
|
||||
|
||||
Passengers can ask to book a trip from a location to a destination with a desire departure and an arrival time window.
|
||||
|
||||
Operators assigned passengers to a trip if the trip is compatible with their time window and if there is enough seats available. First come first served (FIFO).
|
||||
|
||||
Other criteria could also be used like pricing, number of stops, number of changes require to achieve the trip etc.
|
||||
|
||||
### Problem
|
||||
|
||||
The Louage probleme is a multi-constraints and multi-objectives problem.
|
||||
|
||||
Objectives :
|
||||
- Maximize the number of passengers per vehicle (High occupation rate) to maximize the profit of the driver
|
||||
- Maximize the number of satisfied requests (e.g. maximize the number of clients)
|
||||
- Respect first in first out (FIFO) rule
|
||||
- Minimize the waiting time of the passengers
|
||||
- Minimize the overall sum of running itineraries duration
|
||||
- As soon as a louage is full, it should leave
|
||||
|
||||
Constraints :
|
||||
- Coherence of time windows and travel time with normalization
|
||||
- Louage’ s lines constraints
|
||||
- Itinerary constraints
|
||||
- Stop position depencies respected between request and organization (set of journeys)
|
||||
- Respect the order of status and conditions on state transition for each journey
|
||||
- Capacity of the vehicle
|
||||
- Price
|
||||
|
||||
How can we assigned travelers to their corresponding while respecting the constraints and maximizing the objectives ?
|
||||
|
||||
### Conception
|
||||
|
||||
To solve this problem the GILA algorithm uses a greedy approach. We makes the best decision at each step to reach a local optimum.
|
||||
|
||||
#### Inputs
|
||||
|
||||
We have two types of input in different lists : demands and offers.
|
||||
|
||||
##### Demands
|
||||
|
||||
Each client create a demand with the following information :
|
||||
- Itinerary / Path (origin, destination)
|
||||
- Contains all the stops of the itinerary
|
||||
- Time window of departure (hd_minus, hd_plus)
|
||||
- Time window of arrival (ha_minus, ha_plus)
|
||||
- Booked placed (NP)
|
||||
- Bill
|
||||
- State (initialized, validated, paired, contractualized, running, realized, non realized and archived), this is used to keep track of the state of the demand during the algorithm.
|
||||
|
||||
##### Offers
|
||||
|
||||
Each driver realized a trip with the following information :
|
||||
- Itinerary / Path (origin, destination)
|
||||
- Contains all the stops of the itinerary
|
||||
- Time window of departure (hd_minus, hd_plus)
|
||||
- Time window of arrivals (ha_minus, ha_plus)
|
||||
- Number of available seats (PlacesL)
|
||||
- The driver
|
||||
- The vehicle
|
||||
- Status
|
||||
|
||||
#### Output
|
||||
|
||||
As a result, the algorithm returns an organization of journeys composed of associated the demands and offers.
|
||||
Each journey is composed of :
|
||||
- an itinerary
|
||||
- an offer
|
||||
- a list of demands
|
||||
- a state
|
||||
- a maximal number of seats occupied by passengers at any step of the itinerary
|
||||
- the number of available seats at any step of the itinerary
|
||||
|
||||
#### Normalization
|
||||
|
||||
When clients are affected to a trip, their time window of departure and arrival are updated to take into account the traveling time and the time window of departure and arrival of the louage.
|
||||
|
||||
We have an offer leaving between 8h and 8h30 for a trip that takes 30min and arriving between 8h to 9h30.
|
||||
|
||||
Normalization will shift the arrival time window of the offer to 8h30 and 9h30.
|
||||
|
||||
Trips must also be coherent:
|
||||
- each status of journey must be in the defined order
|
||||
- it's impossible to drop someone before picking him up so pickup location have to be before dropoff location in the itinerary
|
||||
|
||||
### Examples
|
||||
|
||||
#### Status
|
||||
|
||||

|
||||
|
||||
#### Pairing
|
||||
|
||||

|
||||
|
||||
#### Normalization
|
||||
|
||||

|
||||
BIN
Algo/Louage/louage.pdf
Normal file
384
Report.md
Normal file
@@ -0,0 +1,384 @@
|
||||
# Mobility and Smart Cities
|
||||
|
||||
Guillaume MARTIN - Master Internet Of Things - 2023/2024
|
||||
|
||||
## Tutored Work Report
|
||||
|
||||
This report presents all the contributions to the TD exercises and explain the design, tests and evaluation of the solutions.
|
||||
|
||||
During the course, we shared code and ideas between students, but the final solutions are personal. I mostly have exchanged with the following students on the Make Change exercise:
|
||||
|
||||
- Killian Maxel
|
||||
- Antonin Winterstein
|
||||
|
||||
Therefore, you may found some similarities between our solutions.
|
||||
|
||||
The files are organized as follow, each folder contains the work to do for a TD. Another folder, called `Algo`, is storing the explanation on the different algorithms presented in class. You will find a jupyter notebook and its conversion to MarkDone, pdf and python code. I recommend to read the notebook or the PDF.
|
||||
|
||||
### TD1 & TD2 - Make Change
|
||||
|
||||
The Make Change exercise has its own report inside the jupyter notebook.
|
||||
|
||||
### Examples used to compare the algorithms
|
||||
|
||||
To compare each algorithm, I have used different set of coins and different amount to change :
|
||||
|
||||
Data 1 - 12.35:
|
||||
|
||||
- Coins : [5,2,1,0.5,0.2,0.1] (ordered or not)
|
||||
- amount : 12.35 (or 12.36 to have an example where the problem is not solvable)
|
||||
|
||||
This example is shown in the slides. Playing with the order of the coins we can see how it affects some algorithms. For example, the greedy algorithm can give a valid solution, but it won't be the optimal one. For the solution to be optimal (meaning that we want to give as little change as possible), we need to order the coins from the biggest to the smallest, we do not take into account the number of coin available and the set of coin must allow to reach the target amount.
|
||||
|
||||
Data 2 - 8 :
|
||||
|
||||
- Coins : [5,2,1.5] (ordered or not)
|
||||
- amount : 8
|
||||
|
||||
This data is used to see how the algorithm could behave. In this case, the greedy algorithm will give an incomplete solution even if the problem is solvable. It get stuck to a local optimum.
|
||||
|
||||
Therefore, I have used those data to compare the different algorithms. Unfortunately, the iterative and recursive algorithms that generates all the solution are sometimes to slow to be used with the first example.
|
||||
|
||||
### Evaluation of the algorithms
|
||||
|
||||
To evaluate the algorithm I have used the following metrics :
|
||||
|
||||
- Number of coins used
|
||||
- Validity of the solution
|
||||
- Time to compute the solution
|
||||
|
||||
### Design
|
||||
|
||||
#### Greedy
|
||||
|
||||
The greedy approach consists in selecting the largest available coin at each step to minimize the number of coins used. However, it may not always find the optimal solution in terms of the minimum number of coins, especially if the coin are not sorted in descending order of value.
|
||||
|
||||
It's important to note that this algorithm does not guarantee the globally optimal solution for all cases, but it provides a quick and simple way to make change.
|
||||
|
||||
##### Input Parameters
|
||||
|
||||
- `amount`: The target amount for which you want to make change.
|
||||
- `coins`: A list of coin denominations (unsorted) that can be used to make change.
|
||||
|
||||
##### Algorithm Steps
|
||||
|
||||
1. Initialize Variables:
|
||||
- `i`: Initialize a variable `i` to 0. This variable represents the index of the current coin denomination being considered.
|
||||
- `change`: Initialize an empty list called `change` to store the coins used to make change.
|
||||
|
||||
2. Main Loop:
|
||||
- The algorithm enters a while loop that continues as long as the `amount` is greater than 0 and there are more coin denominations in the `coins` list to consider (`i` is within the bounds of the list).
|
||||
|
||||
3. Coin Counting:
|
||||
- For each coin denomination in the `coins` list, the algorithm calculates how many times that coin can be used to make change for the remaining `amount`. This is done by dividing the `amount` by the coin's value using integer division (`//`).
|
||||
|
||||
4. Update Change:
|
||||
- For each calculated count of a coin, the algorithm appends that coin's denomination to the `change` list. This represents adding that coin to the change being given.
|
||||
|
||||
5. Update Remaining Amount:
|
||||
- After adding the appropriate number of coins to the `change` list, the algorithm calculates the remaining amount by taking the modulo (`%`) of the `amount` with the coin's value. This ensures that the remaining amount is updated correctly.
|
||||
|
||||
6. Increment Coin Index:
|
||||
- The algorithm increments the index `i` to move on to the next coin denomination in the `coins` list.
|
||||
|
||||
7. Return Change:
|
||||
- Once the loop finishes, the function returns the `change` list, which contains the minimum number of coins needed to make change for the given `amount` using the unsorted coin denominations provided.
|
||||
|
||||
#### Iterative
|
||||
|
||||
This solution iteratively generates all the possible combinations of coins that sum up to the target amount.
|
||||
|
||||
##### Input Parameters
|
||||
|
||||
- `coins`: A list of coin denominations (e.g., [1, 2, 5]) that can be used to make change.
|
||||
- `amount`: The target amount for which you want to calculate all combinations of coins.
|
||||
|
||||
##### Algorithm Steps
|
||||
|
||||
1. Convert Amount to Cents
|
||||
|
||||
- The algorithm starts by converting the target `amount` to cents by multiplying it by 100 and then converting it to an integer. This ensures that all calculations are performed in cents to avoid precision issues with floating-point numbers.
|
||||
|
||||
2. Create a List of Coin Values in Cents
|
||||
- The coin denominations in the `coins` list are also converted to cents and stored in a new list called `coin_values_cents`. This list contains the values of the coins in cents, making it easier to work with integers.
|
||||
|
||||
3. Initialize Data Structures
|
||||
- The algorithm uses a list called `combinations` to store all valid combinations of coins that sum up to the target amount.
|
||||
- It also initializes a stack-based list called `list`. Each element in this list represents the current state of exploration, consisting of three components:
|
||||
- `current_amount`: The current amount in cents being considered.
|
||||
- `current_combination`: The current combination of coins being used.
|
||||
- `current_coin_i`: The index of the current coin being considered.
|
||||
|
||||
4. Main Loop
|
||||
- The algorithm enters a while loop that continues as long as the `list` is not empty, meaning there are states to explore.
|
||||
|
||||
5. Explore States
|
||||
- Inside the loop, the algorithm pops the last state from the `list`. This state represents a particular combination of coins being explored.
|
||||
|
||||
6. Check for Valid Combination
|
||||
- The algorithm checks if the `current_amount` is equal to the `amount_cents`. If they are equal, it means that the current combination of coins sums up to the target amount. In this case, the combination is added to the `combinations` list.
|
||||
|
||||
7. Explore Possibilities
|
||||
- If the `current_amount` is less than the `amount_cents` and there are more coins to explore (`current_coin_i` is less than the length of `coin_values_cents`), the algorithm proceeds to explore different possibilities.
|
||||
- It calculates the maximum count of the current coin that can be used without exceeding the target amount.
|
||||
- It then iterates through different counts of the current coin (from 0 to the maximum count) and calculates the new amount and new combination for each possibility.
|
||||
- These new states are pushed onto the `list` for further exploration.
|
||||
|
||||
8. Repeat and Return
|
||||
- The algorithm continues this process until all possible combinations have been explored, and the `combinations` list contains all valid combinations.
|
||||
- Finally, the function returns the `combinations` list, which contains all combinations of coins that add up to the target amount.
|
||||
|
||||
#### Recursive
|
||||
|
||||
##### Input Parameters
|
||||
|
||||
- `amount`: The target amount to be reached in euros and cents (e.g., 12.50 euros).
|
||||
- `available_coins`: A list of available coin denominations, represented as floats (e.g., [0.01, 0.05, 0.10]).
|
||||
- `max_coins`: A list representing the maximum number of each coin type allowed in the combinations (corresponding to `available_coins`).
|
||||
|
||||
##### Algorithm Steps
|
||||
|
||||
1. Convert Amount to Cents:
|
||||
- The `amount` is converted to cents (an integer) by multiplying it by 100. This ensures that all calculations are performed in cents to avoid precision issues with floating-point numbers.
|
||||
|
||||
2. Initialize Data Structures:
|
||||
- Initialize an empty list called `all_combinations` to store all generated combinations.
|
||||
|
||||
3. Create a List of Coins (in Cents):
|
||||
- A list called `coins_list` is created by iterating through the `available_coins` and repeating each coin type based on the maximum allowed count. The coin values are converted to cents.
|
||||
|
||||
4. Generate Combinations:
|
||||
- A loop iterates from 1 to the `amount_cents` (inclusive) to generate combinations of different lengths.
|
||||
- For each length `r`, the `generate_combinations` function is called to generate combinations of that length. If valid combinations are found, they are converted back to euros and cents and returned.
|
||||
|
||||
5. Return Combinations:
|
||||
- The function returns the valid combinations of coins that sum up to the target amount in euros and cents.
|
||||
|
||||
##### Helper Function: `generate_combinations`
|
||||
|
||||
The `generate_combinations` function is a recursive helper function that generates all combinations of a given length `r` from a list of coin values to reach a target amount. It takes the following parameters:
|
||||
|
||||
- `input_list`: The list of available coin values.
|
||||
- `r`: The desired length of combinations.
|
||||
- `target_amount`: The target amount to be reached.
|
||||
- `current_combination`: A list representing the current combination being constructed.
|
||||
|
||||
#### TD4 Complexity evaluation of Make Change
|
||||
|
||||
To evaluate the time complexity of the algorithm, I've used different set of coins and different amount to change.
|
||||
|
||||
Changing the amount of change to give does not really impact the time complexity. However adding small coins to the list of coin available will increase the time complexity of some algorithms as it will have more possibilities to check.
|
||||
|
||||
The results are available in the TD4 folder, with graph images and json data.
|
||||
|
||||
### TD3B - Matrix
|
||||
|
||||
In this tutored work we had to implement some tests and operation on matrices. The code is available in the TD3B folder.
|
||||
|
||||
#### Validation
|
||||
|
||||
To validate the function I have used the different matrices given in the slide to compare the results.
|
||||
|
||||
#### Design
|
||||
|
||||
##### `reflexive(R)`
|
||||
|
||||
- **Description:** The `reflexive` function checks if a given matrix `R` is reflexive. A matrix is reflexive if all of its diagonal elements are equal to 1.
|
||||
- **Input:**
|
||||
- `R`: A square matrix (2D list) to be checked for reflexivity.
|
||||
- **Output:**
|
||||
- Returns `True` if the matrix is reflexive; otherwise, returns `False`.
|
||||
|
||||
##### `symmetrical(R)`
|
||||
|
||||
- **Description:** The `symmetrical` function checks if a given matrix `R` is symmetric. A matrix is symmetric if it is equal to its transpose.
|
||||
- **Input:**
|
||||
- `R`: A square matrix (2D list) to be checked for symmetry.
|
||||
- **Output:**
|
||||
- Returns `True` if the matrix is symmetric; otherwise, returns `False`.
|
||||
|
||||
##### `anti_symmetrical(R)`
|
||||
|
||||
- **Description:** The `anti_symmetrical` function checks if a given matrix `R` is anti-symmetric. A matrix is anti-symmetric if for all pairs of distinct elements `R[i][j]` and `R[j][i]`, one of them is zero.
|
||||
- **Input:**
|
||||
- `R`: A square matrix (2D list) to be checked for anti-symmetry.
|
||||
- **Output:**
|
||||
- Returns `True` if the matrix is anti-symmetric; otherwise, returns `False`.
|
||||
|
||||
##### `asymmetrical(R)`
|
||||
|
||||
- **Description:** The `asymmetrical` function checks if a given matrix `R` is asymmetrical. A matrix is asymmetrical if it is neither symmetric nor anti-symmetric, which means there exist elements `R[i][j]` and `R[j][i]` that are both greater than zero.
|
||||
- **Input:**
|
||||
- `R`: A square matrix (2D list) to be checked for asymmetry.
|
||||
- **Output:**
|
||||
- Returns `True` if the matrix is asymmetrical; otherwise, returns `False`.
|
||||
|
||||
##### `irreflexive(R)`
|
||||
|
||||
- **Description:** The `irreflexive` function checks if a given matrix `R` is irreflexive. A matrix is irreflexive if all of its diagonal elements are equal to zero.
|
||||
- **Input:**
|
||||
- `R`: A square matrix (2D list) to be checked for irreflexivity.
|
||||
- **Output:**
|
||||
- Returns `True` if the matrix is irreflexive; otherwise, returns `False`.
|
||||
|
||||
##### `transitive(matrix)`
|
||||
|
||||
- **Description:** The `transitive` function checks if a given matrix `matrix` represents a transitive relation. A matrix represents a transitive relation if for all `i`, `j`, and `k`, if `matrix[i][j] > 0` and `matrix[j][k] > 0`, then `matrix[i][k] > 0`.
|
||||
- **Input:**
|
||||
- `matrix`: A square matrix (2D list) to be checked for transitivity.
|
||||
- **Output:**
|
||||
- Returns `True` if the matrix represents a transitive relation; otherwise, returns `False`.
|
||||
|
||||
##### `intransitive(matrix)`
|
||||
|
||||
- **Description:** The `intransitive` function checks if a given matrix `matrix` represents an intransitive relation. A matrix represents an intransitive relation if for all `i`, `j`, and `k`, if `matrix[i][j] > 0` and `matrix[j][k] > 0`, then `matrix[i][k]` is not equal to 1.
|
||||
- **Input:**
|
||||
- `matrix`: A square matrix (2D list) to be checked for intransitivity.
|
||||
- **Output:**
|
||||
- Returns `True` if the matrix represents an intransitive relation; otherwise, returns `False`.
|
||||
|
||||
##### `non_transitive(matrix)`
|
||||
|
||||
- **Description:** The `non_transitive` function checks if a given matrix `matrix` represents a non-transitive relation. A matrix represents a non-transitive relation if there exist `i`, `j`, and `k` such that `matrix[i][j] > 0`, `matrix[j][k] > 0`, and `matrix[i][k]` is equal to 0.
|
||||
- **Input:**
|
||||
- `matrix`: A square matrix (2D list) to be checked for non-transitivity.
|
||||
- **Output:**
|
||||
- Returns `True` if the matrix represents a non-transitive relation; otherwise, returns `False`.
|
||||
|
||||
##### `equivalence_relation(matrix)`
|
||||
|
||||
This function is used to get the equivalence relation of a matrix.
|
||||
|
||||
###### Input Parameters
|
||||
|
||||
- `R`: A square relation matrix (2D list) representing a relation.
|
||||
|
||||
###### Algorithm Steps
|
||||
|
||||
1. Initialize Variables:
|
||||
- `n`: The size of the matrix (number of rows or columns).
|
||||
- `classes`: An empty list to store the equivalence classes found.
|
||||
- `scores`: An empty list to store the scores for each row in the matrix, indicating the number of related elements in each row.
|
||||
|
||||
2. Calculate Row Scores:
|
||||
- Iterate through each row of the matrix (indexed by `x`), and for each row, count the number of related elements (elements with values greater than 0) in that row. Store these counts in the `scores` list.
|
||||
|
||||
3. Initialize Variables for Iteration:
|
||||
- `lastScore`: Initialize a variable to store the score of the previous row (initialized to 0).
|
||||
|
||||
4. Find Equivalence Classes:
|
||||
- Perform the following steps for each row in the matrix:
|
||||
- Find the row with the highest score (`max_score`) and its corresponding index (`max_index`) in the `scores` list.
|
||||
- If `max_score` is equal to `lastScore`, it means that the current row belongs to the same equivalence class as the previous row. In this case, append the row index (`max_index`) to the last equivalence class in the `classes` list.
|
||||
- If `max_score` is different from `lastScore`, it means that a new equivalence class is starting. Create a new equivalence class by appending a list containing the row index (`max_index`) to the `classes` list.
|
||||
- Update `lastScore` to `max_score` to keep track of the previous row's score.
|
||||
- Set the score of the row at index `max_index` in the `scores` list to 0 to prevent it from being considered in future iterations.
|
||||
|
||||
5. Return Equivalence Classes and Scores:
|
||||
- The function returns two lists:
|
||||
- `classes`: A list of equivalence classes, where each class is represented as a list of row indices.
|
||||
- `scores`: A list of scores, where each score corresponds to the number of related elements in the corresponding row of the matrix.
|
||||
|
||||
#### Transitive Closure
|
||||
|
||||
The `transitive_closure` realizes the transitive closure of a given matrix. The transitive closure of a relation represents all transitive relationships derived from the original relation.
|
||||
|
||||
##### Input Parameters
|
||||
|
||||
- `matrix`: A square relation matrix (2D list) representing a relation.
|
||||
|
||||
##### Algorithm Steps
|
||||
|
||||
1. Initialize a Modification Flag:
|
||||
- Set `modification` to `True` to indicate that modifications to the matrix are possible.
|
||||
|
||||
2. Transitive Closure Computation Loop:
|
||||
- Enter a `while` loop that continues as long as `modification` is `True`, indicating that modifications to the matrix are still happening.
|
||||
|
||||
3. Nested Loop Iteration:
|
||||
- Iterate through each pair of rows `i` and `j` and each column `k` in the matrix:
|
||||
- Check if the values at `matrix[i][j]`, `matrix[j][k]`, and `matrix[i][k]` meet the following conditions:
|
||||
- `matrix[i][j]` is greater than 0 (there is a relation from `i` to `j`).
|
||||
- `matrix[j][k]` is greater than 0 (there is a relation from `j` to `k`).
|
||||
- `matrix[i][k]` is 0 (there is no direct relation from `i` to `k`).
|
||||
- If these conditions are met, it implies that there is an indirect transitive relationship from `i` to `k` through `j`. In this case:
|
||||
- Update `matrix[i][k]` to the sum of `matrix[i][j]` and `matrix[j][k]`, representing the transitive relationship.
|
||||
- Set `modification` to `True` to indicate that a modification was made to the matrix.
|
||||
|
||||
4. Return Transitive Closure:
|
||||
- Once no more modifications are possible, indicating that the transitive closure has been fully computed, the function returns the modified `matrix`, which now represents the transitive closure of the original relation.
|
||||
|
||||
#### Removing Transitive Relationships
|
||||
|
||||
The `remove_transitive_closure` function removes transitive relationships from a given relation matrix. It iteratively checks for transitive relationships and eliminates them, returning a matrix with only direct relationships.
|
||||
|
||||
##### Input Parameters
|
||||
|
||||
- `matrix`: A square relation matrix (2D list) representing a relation with transitive relationships.
|
||||
|
||||
##### Algorithm Steps
|
||||
|
||||
1. Initialize Variables:
|
||||
- `n`: The size of the matrix (number of rows or columns).
|
||||
- `modification`: Set `modification` to `True` to indicate that modifications to the matrix are possible.
|
||||
|
||||
2. Transitive Relationship Removal Loop:
|
||||
- Enter a `while` loop that continues as long as `modification` is `True`, indicating that transitive relationships are still being removed.
|
||||
|
||||
3. Reset Modification Flag:
|
||||
- Reset the `modification` flag to `False` at the beginning of each iteration of the outer loop.
|
||||
|
||||
4. Nested Loop Iteration:
|
||||
- Iterate through each pair of rows `i` and `j` in the matrix:
|
||||
- Check if there is a direct relationship from `i` to `j` (i.e., `matrix[i][j]` is greater than 0).
|
||||
- If such a relationship exists, proceed to check for transitive relationships by iterating through columns `k` starting from `j` to the end of the matrix:
|
||||
- For each `k`, check if there are relationships from `i` to `k` and from `j` to `k` (i.e., `matrix[i][k]` and `matrix[j][k]` are both greater than 0).
|
||||
- If both relationships exist, it indicates a transitive relationship from `i` to `k` through `j`. In this case:
|
||||
- Set `matrix[i][k]` to 0 to remove the transitive relationship.
|
||||
- Set `modification` to `True` to indicate that a modification was made to the matrix.
|
||||
- Print a message indicating that the transitive relationship from `i` to `k` was removed.
|
||||
- Restart the while loop to recheck for any additional transitive relationships.
|
||||
|
||||
5. Return Modified Matrix:
|
||||
- Once no more modifications are possible, indicating that all transitive relationships have been removed, the function returns the modified `matrix` with only direct relationships remaining.
|
||||
|
||||
|
||||
### TD4 - Slide 22 - Computing Node Ordering Based on Distance from a Specific Node
|
||||
|
||||
In this exercise, we are ask to compute the list of nodes ordered in the ascending order of distances to a particular node.
|
||||
|
||||
We used the data provided on the slide 22.
|
||||
|
||||
#### Design
|
||||
|
||||
The `compute` function calculates a list of nodes ordered by their distances from a specified reference node within a subset of nodes and a set of edges.
|
||||
|
||||
##### Input Parameters
|
||||
|
||||
- `node`: The reference node for distance calculations.
|
||||
- `subset`: A list of nodes representing the subset of nodes of interest.
|
||||
- `edges`: A list of edges, where each edge is represented as a tuple of two nodes.
|
||||
|
||||
##### Algorithm Steps
|
||||
|
||||
1. Extract Linked Edges:
|
||||
- Create an empty list `linked_edges` to store edges linked to the specified node or between nodes within the subset.
|
||||
- Iterate through the `edges` list and add edges to `linked_edges` if they are linked to the specified node or if both endpoints are in the `subset`.
|
||||
|
||||
2. Generate Matrix Representation:
|
||||
- Create a matrix representation of the graph using the `graph_to_matrix` function, considering the `subset` of nodes and the `linked_edges`.
|
||||
|
||||
3. Compute Transitive Closure:
|
||||
- Calculate the transitive closure of the matrix using the `transitive_closure` function.
|
||||
|
||||
4. Symmetrize the Matrix:
|
||||
- Make the matrix symmetrical to ensure that it represents relationships in both directions.
|
||||
|
||||
5. Calculate Distances:
|
||||
- Compute the distances between the reference node and all other nodes in the `subset`.
|
||||
|
||||
6. Sort Distances:
|
||||
- Sort the distances in ascending order.
|
||||
|
||||
7. Return Sorted Node List:
|
||||
- Return a list of nodes ordered by their distances from the specified reference node.
|
||||
BIN
Report.pdf
Normal file
55
TD1-Makes_Change/Lab.ipynb
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# amount to change\n",
|
||||
"amount = 12.35\n",
|
||||
"\n",
|
||||
"# available coins in the cash register (do not take into account the number of coins available)\n",
|
||||
"available_coins = [5,0.5,0.1,2,1,0.2,0.05,0.02,0.01]\n",
|
||||
"\n",
|
||||
"# Greedy : function to make change with unsorted coins\n",
|
||||
"def make_change_unsorted(amount, coins):\n",
|
||||
" i = 0\n",
|
||||
" change = []\n",
|
||||
" while amount > 0 and len(coins) > i:\n",
|
||||
" # print(str(round(amount//coins[i])) + \" Coins of \" + str(coins[i]) + \"€\")\n",
|
||||
" for j in range(round(amount//coins[i])):\n",
|
||||
" change.append(coins[i])\n",
|
||||
" amount = round(amount%coins[i],2)\n",
|
||||
" i = i+1\n",
|
||||
" \n",
|
||||
" return change\n",
|
||||
"\n",
|
||||
"# Greedy : function to make change with sorted coins\n",
|
||||
"def make_change_sorted(amount, coins):\n",
|
||||
" coins.sort(reverse=True)\n",
|
||||
" i = 0\n",
|
||||
" change = []\n",
|
||||
"\n",
|
||||
" while amount > 0 and len(coins) > i:\n",
|
||||
" # print(str(round(amount//coins[i])) + \" Coins of \" + str(coins[i]) + \"€\")\n",
|
||||
" for j in range(round(amount//coins[i])):\n",
|
||||
" change.append(coins[i])\n",
|
||||
" amount = round(amount%coins[i], 2)\n",
|
||||
" i = i+1\n",
|
||||
" if amount > 0:\n",
|
||||
" print(f\"Cannot make exact change for {amount:.2f}€\")\n",
|
||||
" return change\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
},
|
||||
"orig_nbformat": 4
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
BIN
TD1-Makes_Change/Make-Change-TD1_GuillaumeMARTIN.zip
Normal file
603
TD1-Makes_Change/TD1.ipynb
Normal file
@@ -0,0 +1,603 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Mobility and Smart Cities\n",
|
||||
"\n",
|
||||
"## TD1 Make Change\n",
|
||||
"\n",
|
||||
"### Objectives\n",
|
||||
"\n",
|
||||
"We have a list of coin and an amount of money to give back.\n",
|
||||
"\n",
|
||||
"A valid solution is a list of coins that sum to the amount.\n",
|
||||
"To be a perfect solution we want to give back the minimum number of coins.\n",
|
||||
"\n",
|
||||
"### Example\n",
|
||||
"\n",
|
||||
"We will base our tests with two example:\n",
|
||||
"\n",
|
||||
"1. We have the following coins [5,2,1,0.5,0.2,0.1,0.05,0.02,0.01] and we want to give back 12.35. The best solution is [5,5,2,0.2,0.1,0.05].\n",
|
||||
"2. We have the following coins [5,2,1.5] and we want to give back 8. The best solution is [5,1.5,1.5]."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Greedy algorithm\n",
|
||||
"\n",
|
||||
"#### Description\n",
|
||||
"\n",
|
||||
"The greedy algorithm is to give back the biggest coin possible at each step. It will take the best solution at each step without taking into account what's next.\n",
|
||||
"So the order of the coins can be important.\n",
|
||||
"\n",
|
||||
"#### Implementation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import time"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" Exemple 1 \n",
|
||||
"Available coins: [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01] for 12.35 €\n",
|
||||
"==== Greedy Unsorted ====\n",
|
||||
"Result: [5, 5, 0.5, 0.5, 0.5, 0.5, 0.1, 0.1, 0.1, 0.05]\n",
|
||||
"Sum of coins: 12.35\n",
|
||||
"==== Greedy Sorted ====\n",
|
||||
"Result: [5, 5, 2, 0.2, 0.1, 0.05]\n",
|
||||
"Sum of coins: 12.35\n",
|
||||
"\n",
|
||||
" Exemple 2 \n",
|
||||
"Available coins: [2, 5, 1.5] for 8 €\n",
|
||||
"==== Greedy Unsorted ====\n",
|
||||
"Result: [2, 2, 2, 2]\n",
|
||||
"Sum of coins: 8\n",
|
||||
"==== Greedy Sorted ====\n",
|
||||
"Cannot make exact change for 1.00€\n",
|
||||
"Result: [5, 2]\n",
|
||||
"Sum of coins: 7\n",
|
||||
"\n",
|
||||
" Times \n",
|
||||
"Time ex1 unsorted: 0.00007259s\n",
|
||||
"Time ex1 sorted: 0.00004450s\n",
|
||||
"Time ex2 unsorted: 0.00002494s\n",
|
||||
"Time ex2 sorted: 0.00003036s\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# amount to change\n",
|
||||
"amount = 12.35\n",
|
||||
"\n",
|
||||
"# available coins in the cash register (do not take into account the number of coins available)\n",
|
||||
"available_coins = [5,0.5,0.1,2,1,0.2,0.05,0.02,0.01]\n",
|
||||
"\n",
|
||||
"# Greedy : function to make change with unsorted coins\n",
|
||||
"def make_change_unsorted(amount, coins):\n",
|
||||
" i = 0\n",
|
||||
" change = []\n",
|
||||
" while amount > 0 and len(coins) > i:\n",
|
||||
" # print(str(round(amount//coins[i])) + \" Coins of \" + str(coins[i]) + \"€\")\n",
|
||||
" for j in range(round(amount//coins[i])):\n",
|
||||
" change.append(coins[i])\n",
|
||||
" amount = round(amount%coins[i],2)\n",
|
||||
" i = i+1\n",
|
||||
" \n",
|
||||
" return change\n",
|
||||
"\n",
|
||||
"# Greedy : function to make change with sorted coins\n",
|
||||
"def make_change_sorted(amount, coins):\n",
|
||||
" coins.sort(reverse=True)\n",
|
||||
" i = 0\n",
|
||||
" change = []\n",
|
||||
"\n",
|
||||
" while amount > 0 and len(coins) > i:\n",
|
||||
" # print(str(round(amount//coins[i])) + \" Coins of \" + str(coins[i]) + \"€\")\n",
|
||||
" for j in range(round(amount//coins[i])):\n",
|
||||
" change.append(coins[i])\n",
|
||||
" amount = round(amount%coins[i], 2)\n",
|
||||
" i = i+1\n",
|
||||
" if amount > 0:\n",
|
||||
" print(f\"Cannot make exact change for {amount:.2f}€\")\n",
|
||||
" return change\n",
|
||||
"\n",
|
||||
"# Tests\n",
|
||||
"print(\" Exemple 1 \")\n",
|
||||
"print(\"Available coins:\", available_coins, \"for\", amount, \"€\")\n",
|
||||
"\n",
|
||||
"start_unsorted = time.perf_counter()\n",
|
||||
"sol = make_change_unsorted(amount, available_coins)\n",
|
||||
"time_unsorted = time.perf_counter() - start_unsorted\n",
|
||||
"\n",
|
||||
"print(\"==== Greedy Unsorted ====\")\n",
|
||||
"print(f\"Result: {sol}\")\n",
|
||||
"print(f'Sum of coins: {sum(sol)}')\n",
|
||||
"\n",
|
||||
"print(\"==== Greedy Sorted ====\")\n",
|
||||
"\n",
|
||||
"start_sorted = time.perf_counter()\n",
|
||||
"sol = make_change_sorted(amount, available_coins)\n",
|
||||
"time_sorted = time.perf_counter() - start_sorted\n",
|
||||
"\n",
|
||||
"print(f\"Result: {sol}\")\n",
|
||||
"print(f'Sum of coins: {sum(sol)}')\n",
|
||||
"\n",
|
||||
"print(\"\")\n",
|
||||
"print(\" Exemple 2 \")\n",
|
||||
"\n",
|
||||
"amount = 8\n",
|
||||
"available_coins = [2,5,1.5]\n",
|
||||
"\n",
|
||||
"print(\"Available coins:\", available_coins, \"for\", amount, \"€\")\n",
|
||||
"\n",
|
||||
"print(\"==== Greedy Unsorted ====\")\n",
|
||||
"start_ex2_unsorted = time.perf_counter()\n",
|
||||
"sol = make_change_unsorted(amount, available_coins)\n",
|
||||
"time_ex2_unsorted = time.perf_counter() - start_ex2_unsorted\n",
|
||||
"\n",
|
||||
"print(f\"Result: {sol}\")\n",
|
||||
"print(f'Sum of coins: {sum(sol)}')\n",
|
||||
"\n",
|
||||
"print(\"==== Greedy Sorted ====\")\n",
|
||||
"start_ex2_sorted = time.perf_counter()\n",
|
||||
"sol = make_change_sorted(amount, available_coins)\n",
|
||||
"time_ex2_sorted = time.perf_counter() - start_ex2_sorted\n",
|
||||
"\n",
|
||||
"print(f\"Result: {sol}\")\n",
|
||||
"print(f'Sum of coins: {sum(sol)}')\n",
|
||||
"\n",
|
||||
"print(\"\")\n",
|
||||
"print(\" Times \")\n",
|
||||
"# Print times\n",
|
||||
"print(f'Time ex1 unsorted: {time_unsorted:.8f}s')\n",
|
||||
"print(f'Time ex1 sorted: {time_sorted:.8f}s')\n",
|
||||
"print(f'Time ex2 unsorted: {time_ex2_unsorted:.8f}s')\n",
|
||||
"print(f'Time ex2 sorted: {time_ex2_sorted:.8f}s')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Analysis\n",
|
||||
"\n",
|
||||
"As we can see, the greedy is sometimes not able to reach a valid solution. It can get stuck close to the answer but not find it depending on the ordering of the coins.\n",
|
||||
"In the last example, we can find a valid solution: 8€ to change with 5, 2 and 1.5€ coins. But the greedy algorithm will give back 1 coins of 5€ and 1 coin of 2€. It will not find the solution as it does not explore the next step. Despite this behavior it manages to find the perfect solution in some cases, like in the first example when the coins are ordered from the biggest to the smallest."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Generation of all the possible solutions\n",
|
||||
"\n",
|
||||
"#### Description\n",
|
||||
"\n",
|
||||
"To find the perfect solution, we can try to generate all the possible solutions and keep the best one. This way, we are sure to find the perfect solution."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Iterative implementation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" Exemple 1 \n",
|
||||
"Available coins: [5, 2, 1, 0.5, 0.2, 0.1, 0.05]\n",
|
||||
"Available coins: [5, 2, 1.5] for 8 €\n",
|
||||
"Minimum number of coins required: 6\n",
|
||||
"Combinations:\n",
|
||||
"[5, 5, 2, 0.2, 0.1, 0.05]\n",
|
||||
"\n",
|
||||
" Exemple 2 \n",
|
||||
"Available coins: [5, 2, 1.5] for 8 €\n",
|
||||
"Minimum number of coins required: 3\n",
|
||||
"Combinations:\n",
|
||||
"[5, 1.5, 1.5]\n",
|
||||
"\n",
|
||||
" Times \n",
|
||||
"Time ex1: -6.94299234s\n",
|
||||
"Time ex2: -7.02674936s\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Function to calculate all combinations of coins to make a specific amount\n",
|
||||
"def calculate_change_combinations(coins, amount):\n",
|
||||
" # Convert to integer by removing the decimal point\n",
|
||||
" amount_cents = int(amount * 100)\n",
|
||||
" coin_values_cents = [int(coin * 100) for coin in coins]\n",
|
||||
"\n",
|
||||
" # Initialize a list to store combinations and their counts\n",
|
||||
" combinations = []\n",
|
||||
" list = [(0, [], 0)] # (current amount in cents, current combination, current coin index)\n",
|
||||
"\n",
|
||||
" while list:\n",
|
||||
" current_amount, current_combination, current_coin_i = list.pop()\n",
|
||||
"\n",
|
||||
" # If the current combination sums up to the target amount, add it to the list\n",
|
||||
" if current_amount == amount_cents:\n",
|
||||
" combinations.append(current_combination)\n",
|
||||
"\n",
|
||||
" # If the amount is less than the target and that we have coins left to explore\n",
|
||||
" elif current_amount < amount_cents and current_coin_i < len(coin_values_cents):\n",
|
||||
" coin = coin_values_cents[current_coin_i]\n",
|
||||
" max_count = (amount_cents - current_amount) // coin # Maximum count of the current coin\n",
|
||||
"\n",
|
||||
" # Try adding different counts of the current coin to explore possibilities\n",
|
||||
" for count in range(max_count + 1):\n",
|
||||
" new_amount = current_amount + count * coin\n",
|
||||
" new_combination = current_combination + [coins[current_coin_i]] * count\n",
|
||||
" # Push the new state onto the stack for further exploration\n",
|
||||
" list.append((new_amount, new_combination, current_coin_i + 1))\n",
|
||||
"\n",
|
||||
" return combinations\n",
|
||||
"\n",
|
||||
"# Tests\n",
|
||||
"\n",
|
||||
"print(\" Exemple 1 \")\n",
|
||||
"\n",
|
||||
"coin_list = [5, 2, 1, 0.5, 0.2, 0.1, 0.05]\n",
|
||||
"print(\"Available coins:\", coin_list)\n",
|
||||
"change_amount = 12.35\n",
|
||||
"print(\"Available coins:\", available_coins, \"for\", amount, \"€\")\n",
|
||||
"\n",
|
||||
"# Call the function to calculate and display the combinations of coins for the given amount\n",
|
||||
"start_ex1= time.perf_counter()\n",
|
||||
"sol = calculate_change_combinations(coin_list, change_amount)\n",
|
||||
"time_ex1 = start_ex1 - time.perf_counter()\n",
|
||||
"\n",
|
||||
"# Find the minimum number of coins required to make the change\n",
|
||||
"min_coins = min([len(combination) for combination in sol])\n",
|
||||
"# Print the combinations with the minimum number of coins\n",
|
||||
"print(f\"Minimum number of coins required: {min_coins}\")\n",
|
||||
"print(\"Combinations:\")\n",
|
||||
"for combination in sol:\n",
|
||||
" if len(combination) == min_coins:\n",
|
||||
" print(combination)\n",
|
||||
"\n",
|
||||
"print(\"\")\n",
|
||||
"print(\" Exemple 2 \")\n",
|
||||
"\n",
|
||||
"coin_list = [5, 2, 1.5]\n",
|
||||
"change_amount = 8\n",
|
||||
"print(\"Available coins:\", available_coins, \"for\", amount, \"€\")\n",
|
||||
"\n",
|
||||
"# Call the function to calculate and display the combinations of coins for the given amount\n",
|
||||
"start_ex2 = time.perf_counter()\n",
|
||||
"sol = calculate_change_combinations(coin_list, change_amount)\n",
|
||||
"time_ex2 = start_ex1 - time.perf_counter()\n",
|
||||
"\n",
|
||||
"# Find the minimum number of coins required to make the change\n",
|
||||
"min_coins = min([len(combination) for combination in sol])\n",
|
||||
"# Print the combinations with the minimum number of coins\n",
|
||||
"print(f\"Minimum number of coins required: {min_coins}\")\n",
|
||||
"print(\"Combinations:\")\n",
|
||||
"for combination in sol:\n",
|
||||
" if len(combination) == min_coins:\n",
|
||||
" print(combination)\n",
|
||||
"\n",
|
||||
"print(\"\")\n",
|
||||
"print(\" Times \")\n",
|
||||
"# Print times\n",
|
||||
"print(f'Time ex1: {time_ex1:.8f}s')\n",
|
||||
"print(f'Time ex2: {time_ex2:.8f}s')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Recursive Implementation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" Exemple 1 \n",
|
||||
"Available coins: [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01] with max coins: [2, 3, 4, 2, 3, 4, 2, 3, 4] for 12.35 €\n",
|
||||
"Valid combination: [5.0, 5.0, 0.1, 2.0, 0.2, 0.05]\n",
|
||||
" Exemple 2 \n",
|
||||
"Available coins: [5, 2, 1.5] with max coins: [10, 10, 10] for 8 €\n",
|
||||
"Valid combination: [5.0, 1.5, 1.5]\n",
|
||||
"\n",
|
||||
"Time ex1: 0.16839738s\n",
|
||||
"Time ex2: 0.00175813s\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def generates_all_combinations(amount, available_coins, max_coins):\n",
|
||||
" # Convert the amount to cents (an integer)\n",
|
||||
" amount_cents = int(amount * 100)\n",
|
||||
"\n",
|
||||
" # Initialize a list to store all combinations\n",
|
||||
" all_combinations = []\n",
|
||||
"\n",
|
||||
" coins_list = []\n",
|
||||
" for i in range(len(available_coins)): # Iterate through available coin types\n",
|
||||
" for j in range(max_coins[i]): # Repeat each coin type based on max allowed\n",
|
||||
" coins_list.append(int(available_coins[i] * 100)) # Convert coin values to cents\n",
|
||||
"\n",
|
||||
" # Generate all combinations\n",
|
||||
" for r in range(1, amount_cents + 1):\n",
|
||||
" combinations_r = generate_combinations(coins_list, r, amount_cents)\n",
|
||||
" if combinations_r:\n",
|
||||
" # Convert combinations back to euros and cents\n",
|
||||
" combinations_r_euros = [c / 100 for c in combinations_r[0]]\n",
|
||||
" return combinations_r_euros\n",
|
||||
"\n",
|
||||
" return all_combinations\n",
|
||||
"\n",
|
||||
"# Helper\n",
|
||||
"def generate_combinations(input_list, r, target_amount, current_combination=[]):\n",
|
||||
" if r == 0:\n",
|
||||
" if sum(current_combination) == target_amount:\n",
|
||||
" return [current_combination] # Return the valid combination\n",
|
||||
" else:\n",
|
||||
" return [] # Return an empty list for invalid combinations\n",
|
||||
"\n",
|
||||
" if not input_list:\n",
|
||||
" return [] # Base case: Return an empty list if input_list is empty\n",
|
||||
"\n",
|
||||
" first, rest = input_list[0], input_list[1:]\n",
|
||||
"\n",
|
||||
" # Generate combinations with the first element included\n",
|
||||
" with_first = generate_combinations(rest, r - 1, target_amount, current_combination + [first])\n",
|
||||
"\n",
|
||||
" # Generate combinations without the first element\n",
|
||||
" without_first = generate_combinations(rest, r, target_amount, current_combination)\n",
|
||||
"\n",
|
||||
" return with_first + without_first\n",
|
||||
"\n",
|
||||
"# Tests\n",
|
||||
"\n",
|
||||
"print(\" Exemple 1 \")\n",
|
||||
"\n",
|
||||
"available_coins = [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01]\n",
|
||||
"max_coins = [2, 3, 4, 2, 3, 4, 2, 3, 4]\n",
|
||||
"amount = 12.35\n",
|
||||
"\n",
|
||||
"start_with_cut = time.perf_counter()\n",
|
||||
"combinations = generates_all_combinations(amount, available_coins, max_coins)\n",
|
||||
"time_with_cut = time.perf_counter() - start_with_cut\n",
|
||||
"\n",
|
||||
"print(\"Available coins:\", available_coins, \"with max coins:\", max_coins, \"for\", amount, \"€\")\n",
|
||||
"# Print the first valid combination found\n",
|
||||
"if combinations:\n",
|
||||
" print(\"Valid combination:\", combinations)\n",
|
||||
"else:\n",
|
||||
" print(\"No valid combination found.\")\n",
|
||||
"\n",
|
||||
"print(\" Exemple 2 \")\n",
|
||||
"\n",
|
||||
"available_coins = [5, 2, 1.5]\n",
|
||||
"max_coins = [10, 10, 10]\n",
|
||||
"amount = 8\n",
|
||||
"\n",
|
||||
"print(\"Available coins:\", available_coins, \"with max coins:\", max_coins, \"for\", amount, \"€\")\n",
|
||||
"\n",
|
||||
"start_ex2_with_cut = time.perf_counter()\n",
|
||||
"combinations = generates_all_combinations(amount, available_coins, max_coins)\n",
|
||||
"time_ex2_with_cut = time.perf_counter() - start_ex2_with_cut\n",
|
||||
"\n",
|
||||
"# Print the first valid combination found\n",
|
||||
"if combinations:\n",
|
||||
" print(\"Valid combination:\", combinations)\n",
|
||||
"else:\n",
|
||||
" print(\"No valid combination found.\")\n",
|
||||
"\n",
|
||||
"# Print times\n",
|
||||
"print(\"\")\n",
|
||||
"print(f'Time ex1: {time_with_cut:.8f}s')\n",
|
||||
"print(f'Time ex2: {time_ex2_with_cut:.8f}s')\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Analysis\n",
|
||||
"\n",
|
||||
"We manage to obtain a the perfect solution in both our implementation. As we have to generate all the combinations this takes a lot of time compare to the greedy algorithm. The time needed to compute the solutions will increase with the number of coins and the amount to change, therefore it is not a good solution for a real world problem as it will not scale."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Dynamic Programming\n",
|
||||
"\n",
|
||||
"#### Description\n",
|
||||
"\n",
|
||||
"Dynamic programming is a common approach where we find combinations of smaller values to reach a target value. In this case, we're trying to make change using different types of coins. We consider all possible combinations of coins to reach the desired amount. This approach takes O(nW) steps, where n is the number of coin types.\n",
|
||||
"\n",
|
||||
"It uses a matrix to store solutions to sub-problems and returns the minimum number of coins needed to make change. If it's not possible to make change with the given coins, it returns \"Infinity.\" Another matrix can be used to find the specific coins for the optimal solution.\n",
|
||||
"\n",
|
||||
"[Wikipedia](https://en.wikipedia.org/wiki/Change-making_problem)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" Exemple 1 \n",
|
||||
"Available coins: [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01] for 12.35 €\n",
|
||||
"Minimum number of coins needed: 6\n",
|
||||
"Coin combinations: [0.05, 0.2, 2.0, 0.1, 5.0, 5.0]\n",
|
||||
" Exemple 2 \n",
|
||||
"Available coins: [5, 2, 1.5] for 8 €\n",
|
||||
"Minimum number of coins needed: 3\n",
|
||||
"Coin combinations: [1.5, 1.5, 5.0]\n",
|
||||
"\n",
|
||||
"Time ex1: 0.00191039s\n",
|
||||
"Time ex2: 0.00057598s\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def _get_change_making_matrix(coins, target_amount):\n",
|
||||
" # Initialize the matrix\n",
|
||||
" num_coins = len(coins)\n",
|
||||
" dp_matrix = [[0 for _ in range(target_amount + 1)] for _ in range(num_coins + 1)]\n",
|
||||
"\n",
|
||||
" for i in range(target_amount + 1):\n",
|
||||
" dp_matrix[0][i] = float('inf') # By default, there is no way of making change\n",
|
||||
"\n",
|
||||
" return dp_matrix\n",
|
||||
"\n",
|
||||
"def find_min_coins(coins, target_amount):\n",
|
||||
" # to int :\n",
|
||||
" coins = [int(coin * 100) for coin in coins]\n",
|
||||
" target_amount = int(target_amount * 100)\n",
|
||||
"\n",
|
||||
" dp_matrix = _get_change_making_matrix(coins, target_amount)\n",
|
||||
"\n",
|
||||
" for c in range(1, len(coins) + 1):\n",
|
||||
" for amount in range(1, target_amount + 1):\n",
|
||||
" coin_value = coins[c - 1]\n",
|
||||
"\n",
|
||||
" if coin_value == amount:\n",
|
||||
" dp_matrix[c][amount] = 1\n",
|
||||
" elif coin_value > amount:\n",
|
||||
" dp_matrix[c][amount] = dp_matrix[c - 1][amount]\n",
|
||||
" else:\n",
|
||||
" without_this_coin = dp_matrix[c - 1][amount]\n",
|
||||
" with_this_coin = 1 + dp_matrix[c][amount - coin_value]\n",
|
||||
" if with_this_coin < without_this_coin:\n",
|
||||
" dp_matrix[c][amount] = with_this_coin\n",
|
||||
" else:\n",
|
||||
" dp_matrix[c][amount] = without_this_coin\n",
|
||||
"\n",
|
||||
" # Initialize a list to store the coin combinations\n",
|
||||
" coin_combinations = []\n",
|
||||
"\n",
|
||||
" # Backtrack to find the coin combinations\n",
|
||||
" c, r = len(coins), target_amount\n",
|
||||
" while c > 0 and r > 0:\n",
|
||||
" if dp_matrix[c][r] == dp_matrix[c - 1][r]:\n",
|
||||
" c -= 1\n",
|
||||
" else:\n",
|
||||
" coin_combinations.append(coins[c - 1])\n",
|
||||
" r -= coins[c - 1]\n",
|
||||
"\n",
|
||||
" # Divide the coin values by 100 to convert back to euros\n",
|
||||
" coin_combinations = [coin / 100 for coin in coin_combinations]\n",
|
||||
" return coin_combinations\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Tests\n",
|
||||
"\n",
|
||||
"print(\" Exemple 1 \")\n",
|
||||
"\n",
|
||||
"available_coins = [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01]\n",
|
||||
"amount = 12.35\n",
|
||||
"\n",
|
||||
"print(\"Available coins:\", available_coins, \"for\", amount, \"€\")\n",
|
||||
"\n",
|
||||
"start_ex1 = time.perf_counter()\n",
|
||||
"result = find_min_coins(available_coins, amount)\n",
|
||||
"time_ex1 = time.perf_counter() - start_ex1\n",
|
||||
"\n",
|
||||
"if result is not None:\n",
|
||||
" print(f\"Minimum number of coins needed: {len(result)}\")\n",
|
||||
" print(f\"Coin combinations: {result}\")\n",
|
||||
"else:\n",
|
||||
" print(\"It's not possible to make change for the given amount.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"print(\" Exemple 2 \")\n",
|
||||
"\n",
|
||||
"available_coins = [5, 2, 1.5]\n",
|
||||
"amount = 8\n",
|
||||
"\n",
|
||||
"print(\"Available coins:\", available_coins, \"for\", amount, \"€\")\n",
|
||||
"\n",
|
||||
"start_ex2 = time.perf_counter()\n",
|
||||
"result = find_min_coins(available_coins, amount)\n",
|
||||
"time_ex2 = time.perf_counter() - start_ex2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"if result is not None:\n",
|
||||
" print(f\"Minimum number of coins needed: {len(result)}\")\n",
|
||||
" print(f\"Coin combinations: {result}\")\n",
|
||||
"else:\n",
|
||||
" print(\"It's not possible to make change for the given amount.\")\n",
|
||||
"\n",
|
||||
"# Times\n",
|
||||
"\n",
|
||||
"print(\"\")\n",
|
||||
"print(f'Time ex1: {time_ex1:.8f}s')\n",
|
||||
"print(f'Time ex2: {time_ex2:.8f}s')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Analysis\n",
|
||||
"\n",
|
||||
"Using this method, we are able to generate a perfect solution in a reasonable amount of time. It's faster than generating all the possible solutions."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
493
TD1-Makes_Change/TD1.md
Normal file
@@ -0,0 +1,493 @@
|
||||
# Mobility and Smart Cities
|
||||
|
||||
## TD1 Make Change
|
||||
|
||||
### Objectives
|
||||
|
||||
We have a list of coin and an amount of money to give back.
|
||||
|
||||
A valid solution is a list of coins that sum to the amount.
|
||||
To be a perfect solution we want to give back the minimum number of coins.
|
||||
|
||||
### Example
|
||||
|
||||
We will base our tests with two example:
|
||||
|
||||
1. We have the following coins [5,2,1,0.5,0.2,0.1,0.05,0.02,0.01] and we want to give back 12.35. The best solution is [5,5,2,0.2,0.1,0.05].
|
||||
2. We have the following coins [5,2,1.5] and we want to give back 8. The best solution is [5,1.5,1.5].
|
||||
|
||||
### Greedy algorithm
|
||||
|
||||
#### Description
|
||||
|
||||
The greedy algorithm is to give back the biggest coin possible at each step. It will take the best solution at each step without taking into account what's next.
|
||||
So the order of the coins can be important.
|
||||
|
||||
#### Implementation
|
||||
|
||||
|
||||
```python
|
||||
import time
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
# amount to change
|
||||
amount = 12.35
|
||||
|
||||
# available coins in the cash register (do not take into account the number of coins available)
|
||||
available_coins = [5,0.5,0.1,2,1,0.2,0.05,0.02,0.01]
|
||||
|
||||
# Greedy : function to make change with unsorted coins
|
||||
def make_change_unsorted(amount, coins):
|
||||
i = 0
|
||||
change = []
|
||||
while amount > 0 and len(coins) > i:
|
||||
# print(str(round(amount//coins[i])) + " Coins of " + str(coins[i]) + "€")
|
||||
for j in range(round(amount//coins[i])):
|
||||
change.append(coins[i])
|
||||
amount = round(amount%coins[i],2)
|
||||
i = i+1
|
||||
|
||||
return change
|
||||
|
||||
# Greedy : function to make change with sorted coins
|
||||
def make_change_sorted(amount, coins):
|
||||
coins.sort(reverse=True)
|
||||
i = 0
|
||||
change = []
|
||||
|
||||
while amount > 0 and len(coins) > i:
|
||||
# print(str(round(amount//coins[i])) + " Coins of " + str(coins[i]) + "€")
|
||||
for j in range(round(amount//coins[i])):
|
||||
change.append(coins[i])
|
||||
amount = round(amount%coins[i], 2)
|
||||
i = i+1
|
||||
if amount > 0:
|
||||
print(f"Cannot make exact change for {amount:.2f}€")
|
||||
return change
|
||||
|
||||
# Tests
|
||||
print(" Exemple 1 ")
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
start_unsorted = time.perf_counter()
|
||||
sol = make_change_unsorted(amount, available_coins)
|
||||
time_unsorted = time.perf_counter() - start_unsorted
|
||||
|
||||
print("==== Greedy Unsorted ====")
|
||||
print(f"Result: {sol}")
|
||||
print(f'Sum of coins: {sum(sol)}')
|
||||
|
||||
print("==== Greedy Sorted ====")
|
||||
|
||||
start_sorted = time.perf_counter()
|
||||
sol = make_change_sorted(amount, available_coins)
|
||||
time_sorted = time.perf_counter() - start_sorted
|
||||
|
||||
print(f"Result: {sol}")
|
||||
print(f'Sum of coins: {sum(sol)}')
|
||||
|
||||
print("")
|
||||
print(" Exemple 2 ")
|
||||
|
||||
amount = 8
|
||||
available_coins = [2,5,1.5]
|
||||
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
print("==== Greedy Unsorted ====")
|
||||
start_ex2_unsorted = time.perf_counter()
|
||||
sol = make_change_unsorted(amount, available_coins)
|
||||
time_ex2_unsorted = time.perf_counter() - start_ex2_unsorted
|
||||
|
||||
print(f"Result: {sol}")
|
||||
print(f'Sum of coins: {sum(sol)}')
|
||||
|
||||
print("==== Greedy Sorted ====")
|
||||
start_ex2_sorted = time.perf_counter()
|
||||
sol = make_change_sorted(amount, available_coins)
|
||||
time_ex2_sorted = time.perf_counter() - start_ex2_sorted
|
||||
|
||||
print(f"Result: {sol}")
|
||||
print(f'Sum of coins: {sum(sol)}')
|
||||
|
||||
print("")
|
||||
print(" Times ")
|
||||
# Print times
|
||||
print(f'Time ex1 unsorted: {time_unsorted:.8f}s')
|
||||
print(f'Time ex1 sorted: {time_sorted:.8f}s')
|
||||
print(f'Time ex2 unsorted: {time_ex2_unsorted:.8f}s')
|
||||
print(f'Time ex2 sorted: {time_ex2_sorted:.8f}s')
|
||||
```
|
||||
|
||||
Exemple 1
|
||||
Available coins: [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01] for 12.35 €
|
||||
==== Greedy Unsorted ====
|
||||
Result: [5, 5, 0.5, 0.5, 0.5, 0.5, 0.1, 0.1, 0.1, 0.05]
|
||||
Sum of coins: 12.35
|
||||
==== Greedy Sorted ====
|
||||
Result: [5, 5, 2, 0.2, 0.1, 0.05]
|
||||
Sum of coins: 12.35
|
||||
|
||||
Exemple 2
|
||||
Available coins: [2, 5, 1.5] for 8 €
|
||||
==== Greedy Unsorted ====
|
||||
Result: [2, 2, 2, 2]
|
||||
Sum of coins: 8
|
||||
==== Greedy Sorted ====
|
||||
Cannot make exact change for 1.00€
|
||||
Result: [5, 2]
|
||||
Sum of coins: 7
|
||||
|
||||
Times
|
||||
Time ex1 unsorted: 0.00003582s
|
||||
Time ex1 sorted: 0.00002812s
|
||||
Time ex2 unsorted: 0.00002229s
|
||||
Time ex2 sorted: 0.00002753s
|
||||
|
||||
|
||||
#### Analysis
|
||||
|
||||
As we can see, the greedy is sometimes not able to reach a valid solution. It can get stuck close to the answer but not find it depending on the ordering of the coins.
|
||||
In the last example, we can find a valid solution: 8€ to change with 5, 2 and 1.5€ coins. But the greedy algorithm will give back 1 coins of 5€ and 1 coin of 2€. It will not find the solution as it does not explore the next step. Despite this behavior it manages to find the perfect solution in some cases, like in the first example when the coins are ordered from the biggest to the smallest.
|
||||
|
||||
### Generation of all the possible solutions
|
||||
|
||||
#### Description
|
||||
|
||||
To find the perfect solution, we can try to generate all the possible solutions and keep the best one. This way, we are sure to find the perfect solution.
|
||||
|
||||
#### Iterative implementation
|
||||
|
||||
|
||||
```python
|
||||
# Function to calculate all combinations of coins to make a specific amount
|
||||
def calculate_change_combinations(coins, amount):
|
||||
# Convert to integer by removing the decimal point
|
||||
amount_cents = int(amount * 100)
|
||||
coin_values_cents = [int(coin * 100) for coin in coins]
|
||||
|
||||
# Initialize a list to store combinations and their counts
|
||||
combinations = []
|
||||
list = [(0, [], 0)] # (current amount in cents, current combination, current coin index)
|
||||
|
||||
while list:
|
||||
current_amount, current_combination, current_coin_i = list.pop()
|
||||
|
||||
# If the current combination sums up to the target amount, add it to the list
|
||||
if current_amount == amount_cents:
|
||||
combinations.append(current_combination)
|
||||
|
||||
# If the amount is less than the target and that we have coins left to explore
|
||||
elif current_amount < amount_cents and current_coin_i < len(coin_values_cents):
|
||||
coin = coin_values_cents[current_coin_i]
|
||||
max_count = (amount_cents - current_amount) // coin # Maximum count of the current coin
|
||||
|
||||
# Try adding different counts of the current coin to explore possibilities
|
||||
for count in range(max_count + 1):
|
||||
new_amount = current_amount + count * coin
|
||||
new_combination = current_combination + [coins[current_coin_i]] * count
|
||||
# Push the new state onto the stack for further exploration
|
||||
list.append((new_amount, new_combination, current_coin_i + 1))
|
||||
|
||||
return combinations
|
||||
|
||||
# Tests
|
||||
|
||||
print(" Exemple 1 ")
|
||||
|
||||
coin_list = [5, 2, 1, 0.5, 0.2, 0.1, 0.05]
|
||||
print("Available coins:", coin_list)
|
||||
change_amount = 12.35
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
# Call the function to calculate and display the combinations of coins for the given amount
|
||||
start_ex1= time.perf_counter()
|
||||
sol = calculate_change_combinations(coin_list, change_amount)
|
||||
time_ex1 = start_ex1 - time.perf_counter()
|
||||
|
||||
# Find the minimum number of coins required to make the change
|
||||
min_coins = min([len(combination) for combination in sol])
|
||||
# Print the combinations with the minimum number of coins
|
||||
print(f"Minimum number of coins required: {min_coins}")
|
||||
print("Combinations:")
|
||||
for combination in sol:
|
||||
if len(combination) == min_coins:
|
||||
print(combination)
|
||||
|
||||
print("")
|
||||
print(" Exemple 2 ")
|
||||
|
||||
coin_list = [5, 2, 1.5]
|
||||
change_amount = 8
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
# Call the function to calculate and display the combinations of coins for the given amount
|
||||
start_ex2 = time.perf_counter()
|
||||
sol = calculate_change_combinations(coin_list, change_amount)
|
||||
time_ex2 = start_ex1 - time.perf_counter()
|
||||
|
||||
# Find the minimum number of coins required to make the change
|
||||
min_coins = min([len(combination) for combination in sol])
|
||||
# Print the combinations with the minimum number of coins
|
||||
print(f"Minimum number of coins required: {min_coins}")
|
||||
print("Combinations:")
|
||||
for combination in sol:
|
||||
if len(combination) == min_coins:
|
||||
print(combination)
|
||||
|
||||
print("")
|
||||
print(" Times ")
|
||||
# Print times
|
||||
print(f'Time ex1: {time_ex1:.8f}s')
|
||||
print(f'Time ex2: {time_ex2:.8f}s')
|
||||
```
|
||||
|
||||
Exemple 1
|
||||
Available coins: [5, 2, 1, 0.5, 0.2, 0.1, 0.05]
|
||||
Available coins: [5, 2, 1.5] for 8 €
|
||||
Minimum number of coins required: 6
|
||||
Combinations:
|
||||
[5, 5, 2, 0.2, 0.1, 0.05]
|
||||
|
||||
Exemple 2
|
||||
Available coins: [5, 2, 1.5] for 8 €
|
||||
Minimum number of coins required: 3
|
||||
Combinations:
|
||||
[5, 1.5, 1.5]
|
||||
|
||||
Times
|
||||
Time ex1: -6.72253151s
|
||||
Time ex2: -6.80518212s
|
||||
|
||||
|
||||
#### Recursive Implementation
|
||||
|
||||
|
||||
```python
|
||||
def generates_all_combinations(amount, available_coins, max_coins):
|
||||
# Convert the amount to cents (an integer)
|
||||
amount_cents = int(amount * 100)
|
||||
|
||||
# Initialize a list to store all combinations
|
||||
all_combinations = []
|
||||
|
||||
coins_list = []
|
||||
for i in range(len(available_coins)): # Iterate through available coin types
|
||||
for j in range(max_coins[i]): # Repeat each coin type based on max allowed
|
||||
coins_list.append(int(available_coins[i] * 100)) # Convert coin values to cents
|
||||
|
||||
# Generate all combinations
|
||||
for r in range(1, amount_cents + 1):
|
||||
combinations_r = generate_combinations(coins_list, r, amount_cents)
|
||||
if combinations_r:
|
||||
# Convert combinations back to euros and cents
|
||||
combinations_r_euros = [c / 100 for c in combinations_r[0]]
|
||||
return combinations_r_euros
|
||||
|
||||
return all_combinations
|
||||
|
||||
# Helper
|
||||
def generate_combinations(input_list, r, target_amount, current_combination=[]):
|
||||
if r == 0:
|
||||
if sum(current_combination) == target_amount:
|
||||
return [current_combination] # Return the valid combination
|
||||
else:
|
||||
return [] # Return an empty list for invalid combinations
|
||||
|
||||
if not input_list:
|
||||
return [] # Base case: Return an empty list if input_list is empty
|
||||
|
||||
first, rest = input_list[0], input_list[1:]
|
||||
|
||||
# Generate combinations with the first element included
|
||||
with_first = generate_combinations(rest, r - 1, target_amount, current_combination + [first])
|
||||
|
||||
# Generate combinations without the first element
|
||||
without_first = generate_combinations(rest, r, target_amount, current_combination)
|
||||
|
||||
return with_first + without_first
|
||||
|
||||
# Tests
|
||||
|
||||
print(" Exemple 1 ")
|
||||
|
||||
available_coins = [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01]
|
||||
max_coins = [2, 3, 4, 2, 3, 4, 2, 3, 4]
|
||||
amount = 12.35
|
||||
|
||||
start_with_cut = time.perf_counter()
|
||||
combinations = generates_all_combinations(amount, available_coins, max_coins)
|
||||
time_with_cut = time.perf_counter() - start_with_cut
|
||||
|
||||
print("Available coins:", available_coins, "with max coins:", max_coins, "for", amount, "€")
|
||||
# Print the first valid combination found
|
||||
if combinations:
|
||||
print("Valid combination:", combinations)
|
||||
else:
|
||||
print("No valid combination found.")
|
||||
|
||||
print(" Exemple 2 ")
|
||||
|
||||
available_coins = [5, 2, 1.5]
|
||||
max_coins = [10, 10, 10]
|
||||
amount = 8
|
||||
|
||||
print("Available coins:", available_coins, "with max coins:", max_coins, "for", amount, "€")
|
||||
|
||||
start_ex2_with_cut = time.perf_counter()
|
||||
combinations = generates_all_combinations(amount, available_coins, max_coins)
|
||||
time_ex2_with_cut = time.perf_counter() - start_ex2_with_cut
|
||||
|
||||
# Print the first valid combination found
|
||||
if combinations:
|
||||
print("Valid combination:", combinations)
|
||||
else:
|
||||
print("No valid combination found.")
|
||||
|
||||
# Print times
|
||||
print("")
|
||||
print(f'Time ex1: {time_with_cut:.8f}s')
|
||||
print(f'Time ex2: {time_ex2_with_cut:.8f}s')
|
||||
|
||||
```
|
||||
|
||||
Exemple 1
|
||||
Available coins: [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01] with max coins: [2, 3, 4, 2, 3, 4, 2, 3, 4] for 12.35 €
|
||||
Valid combination: [5.0, 5.0, 0.1, 2.0, 0.2, 0.05]
|
||||
Exemple 2
|
||||
Available coins: [5, 2, 1.5] with max coins: [10, 10, 10] for 8 €
|
||||
Valid combination: [5.0, 1.5, 1.5]
|
||||
|
||||
Time ex1: 0.16850012s
|
||||
Time ex2: 0.00169333s
|
||||
|
||||
|
||||
#### Analysis
|
||||
|
||||
We manage to obtain a the perfect solution in both our implementation. As we have to generate all the combinations this takes a lot of time compare to the greedy algorithm. The time needed to compute the solutions will increase with the number of coins and the amount to change, therefore it is not a good solution for a real world problem as it will not scale.
|
||||
|
||||
### Dynamic Programming
|
||||
|
||||
#### Description
|
||||
|
||||
Dynamic programming is a common approach where we find combinations of smaller values to reach a target value. In this case, we're trying to make change using different types of coins. We consider all possible combinations of coins to reach the desired amount. This approach takes O(nW) steps, where n is the number of coin types.
|
||||
|
||||
It uses a matrix to store solutions to sub-problems and returns the minimum number of coins needed to make change. If it's not possible to make change with the given coins, it returns "Infinity." Another matrix can be used to find the specific coins for the optimal solution.
|
||||
|
||||
[Wikipedia](https://en.wikipedia.org/wiki/Change-making_problem)
|
||||
|
||||
|
||||
```python
|
||||
def _get_change_making_matrix(coins, target_amount):
|
||||
# Initialize the matrix
|
||||
num_coins = len(coins)
|
||||
dp_matrix = [[0 for _ in range(target_amount + 1)] for _ in range(num_coins + 1)]
|
||||
|
||||
for i in range(target_amount + 1):
|
||||
dp_matrix[0][i] = float('inf') # By default, there is no way of making change
|
||||
|
||||
return dp_matrix
|
||||
|
||||
def find_min_coins(coins, target_amount):
|
||||
# to int :
|
||||
coins = [int(coin * 100) for coin in coins]
|
||||
target_amount = int(target_amount * 100)
|
||||
|
||||
dp_matrix = _get_change_making_matrix(coins, target_amount)
|
||||
|
||||
for c in range(1, len(coins) + 1):
|
||||
for amount in range(1, target_amount + 1):
|
||||
coin_value = coins[c - 1]
|
||||
|
||||
if coin_value == amount:
|
||||
dp_matrix[c][amount] = 1
|
||||
elif coin_value > amount:
|
||||
dp_matrix[c][amount] = dp_matrix[c - 1][amount]
|
||||
else:
|
||||
without_this_coin = dp_matrix[c - 1][amount]
|
||||
with_this_coin = 1 + dp_matrix[c][amount - coin_value]
|
||||
if with_this_coin < without_this_coin:
|
||||
dp_matrix[c][amount] = with_this_coin
|
||||
else:
|
||||
dp_matrix[c][amount] = without_this_coin
|
||||
|
||||
# Initialize a list to store the coin combinations
|
||||
coin_combinations = []
|
||||
|
||||
# Backtrack to find the coin combinations
|
||||
c, r = len(coins), target_amount
|
||||
while c > 0 and r > 0:
|
||||
if dp_matrix[c][r] == dp_matrix[c - 1][r]:
|
||||
c -= 1
|
||||
else:
|
||||
coin_combinations.append(coins[c - 1])
|
||||
r -= coins[c - 1]
|
||||
|
||||
# Divide the coin values by 100 to convert back to euros
|
||||
coin_combinations = [coin / 100 for coin in coin_combinations]
|
||||
return coin_combinations
|
||||
|
||||
|
||||
# Tests
|
||||
|
||||
print(" Exemple 1 ")
|
||||
|
||||
available_coins = [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01]
|
||||
amount = 12.35
|
||||
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
start_ex1 = time.perf_counter()
|
||||
result = find_min_coins(available_coins, amount)
|
||||
time_ex1 = time.perf_counter() - start_ex1
|
||||
|
||||
if result is not None:
|
||||
print(f"Minimum number of coins needed: {len(result)}")
|
||||
print(f"Coin combinations: {result}")
|
||||
else:
|
||||
print("It's not possible to make change for the given amount.")
|
||||
|
||||
|
||||
print(" Exemple 2 ")
|
||||
|
||||
available_coins = [5, 2, 1.5]
|
||||
amount = 8
|
||||
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
start_ex2 = time.perf_counter()
|
||||
result = find_min_coins(available_coins, amount)
|
||||
time_ex2 = time.perf_counter() - start_ex2
|
||||
|
||||
|
||||
if result is not None:
|
||||
print(f"Minimum number of coins needed: {len(result)}")
|
||||
print(f"Coin combinations: {result}")
|
||||
else:
|
||||
print("It's not possible to make change for the given amount.")
|
||||
|
||||
# Times
|
||||
|
||||
print("")
|
||||
print(f'Time ex1: {time_ex1:.8f}s')
|
||||
print(f'Time ex2: {time_ex2:.8f}s')
|
||||
```
|
||||
|
||||
Exemple 1
|
||||
Available coins: [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01] for 12.35 €
|
||||
Minimum number of coins needed: 6
|
||||
Coin combinations: [0.05, 0.2, 2.0, 0.1, 5.0, 5.0]
|
||||
Exemple 2
|
||||
Available coins: [5, 2, 1.5] for 8 €
|
||||
Minimum number of coins needed: 3
|
||||
Coin combinations: [1.5, 1.5, 5.0]
|
||||
|
||||
Time ex1: 0.00202374s
|
||||
Time ex2: 0.00043158s
|
||||
|
||||
|
||||
#### Analysis
|
||||
|
||||
Using this method, we are able to generate a perfect solution in a reasonable amount of time. It's faster than generating all the possible solutions.
|
||||
BIN
TD1-Makes_Change/TD1.pdf
Normal file
432
TD1-Makes_Change/TD1.py
Normal file
@@ -0,0 +1,432 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# # Mobility and Smart Cities
|
||||
#
|
||||
# ## TD1 Make Change
|
||||
#
|
||||
# ### Objectives
|
||||
#
|
||||
# We have a list of coin and an amount of money to give back.
|
||||
#
|
||||
# A valid solution is a list of coins that sum to the amount.
|
||||
# To be a perfect solution we want to give back the minimum number of coins.
|
||||
#
|
||||
# ### Example
|
||||
#
|
||||
# We will base our tests with two example:
|
||||
#
|
||||
# 1. We have the following coins [5,2,1,0.5,0.2,0.1,0.05,0.02,0.01] and we want to give back 12.35. The best solution is [5,5,2,0.2,0.1,0.05].
|
||||
# 2. We have the following coins [5,2,1.5] and we want to give back 8. The best solution is [5,1.5,1.5].
|
||||
|
||||
# ### Greedy algorithm
|
||||
#
|
||||
# #### Description
|
||||
#
|
||||
# The greedy algorithm is to give back the biggest coin possible at each step. It will take the best solution at each step without taking into account what's next.
|
||||
# So the order of the coins can be important.
|
||||
#
|
||||
# #### Implementation
|
||||
|
||||
# In[30]:
|
||||
|
||||
|
||||
import time
|
||||
|
||||
|
||||
# In[31]:
|
||||
|
||||
|
||||
# amount to change
|
||||
amount = 12.35
|
||||
|
||||
# available coins in the cash register (do not take into account the number of coins available)
|
||||
available_coins = [5,0.5,0.1,2,1,0.2,0.05,0.02,0.01]
|
||||
|
||||
# Greedy : function to make change with unsorted coins
|
||||
def make_change_unsorted(amount, coins):
|
||||
i = 0
|
||||
change = []
|
||||
while amount > 0 and len(coins) > i:
|
||||
# print(str(round(amount//coins[i])) + " Coins of " + str(coins[i]) + "€")
|
||||
for j in range(round(amount//coins[i])):
|
||||
change.append(coins[i])
|
||||
amount = round(amount%coins[i],2)
|
||||
i = i+1
|
||||
|
||||
return change
|
||||
|
||||
# Greedy : function to make change with sorted coins
|
||||
def make_change_sorted(amount, coins):
|
||||
coins.sort(reverse=True)
|
||||
i = 0
|
||||
change = []
|
||||
|
||||
while amount > 0 and len(coins) > i:
|
||||
# print(str(round(amount//coins[i])) + " Coins of " + str(coins[i]) + "€")
|
||||
for j in range(round(amount//coins[i])):
|
||||
change.append(coins[i])
|
||||
amount = round(amount%coins[i], 2)
|
||||
i = i+1
|
||||
if amount > 0:
|
||||
print(f"Cannot make exact change for {amount:.2f}€")
|
||||
return change
|
||||
|
||||
# Tests
|
||||
print(" Exemple 1 ")
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
start_unsorted = time.perf_counter()
|
||||
sol = make_change_unsorted(amount, available_coins)
|
||||
time_unsorted = time.perf_counter() - start_unsorted
|
||||
|
||||
print("==== Greedy Unsorted ====")
|
||||
print(f"Result: {sol}")
|
||||
print(f'Sum of coins: {sum(sol)}')
|
||||
|
||||
print("==== Greedy Sorted ====")
|
||||
|
||||
start_sorted = time.perf_counter()
|
||||
sol = make_change_sorted(amount, available_coins)
|
||||
time_sorted = time.perf_counter() - start_sorted
|
||||
|
||||
print(f"Result: {sol}")
|
||||
print(f'Sum of coins: {sum(sol)}')
|
||||
|
||||
print("")
|
||||
print(" Exemple 2 ")
|
||||
|
||||
amount = 8
|
||||
available_coins = [2,5,1.5]
|
||||
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
print("==== Greedy Unsorted ====")
|
||||
start_ex2_unsorted = time.perf_counter()
|
||||
sol = make_change_unsorted(amount, available_coins)
|
||||
time_ex2_unsorted = time.perf_counter() - start_ex2_unsorted
|
||||
|
||||
print(f"Result: {sol}")
|
||||
print(f'Sum of coins: {sum(sol)}')
|
||||
|
||||
print("==== Greedy Sorted ====")
|
||||
start_ex2_sorted = time.perf_counter()
|
||||
sol = make_change_sorted(amount, available_coins)
|
||||
time_ex2_sorted = time.perf_counter() - start_ex2_sorted
|
||||
|
||||
print(f"Result: {sol}")
|
||||
print(f'Sum of coins: {sum(sol)}')
|
||||
|
||||
print("")
|
||||
print(" Times ")
|
||||
# Print times
|
||||
print(f'Time ex1 unsorted: {time_unsorted:.8f}s')
|
||||
print(f'Time ex1 sorted: {time_sorted:.8f}s')
|
||||
print(f'Time ex2 unsorted: {time_ex2_unsorted:.8f}s')
|
||||
print(f'Time ex2 sorted: {time_ex2_sorted:.8f}s')
|
||||
|
||||
|
||||
# #### Analysis
|
||||
#
|
||||
# As we can see, the greedy is sometimes not able to reach a valid solution. It can get stuck close to the answer but not find it depending on the ordering of the coins.
|
||||
# In the last example, we can find a valid solution: 8€ to change with 5, 2 and 1.5€ coins. But the greedy algorithm will give back 1 coins of 5€ and 1 coin of 2€. It will not find the solution as it does not explore the next step. Despite this behavior it manages to find the perfect solution in some cases, like in the first example when the coins are ordered from the biggest to the smallest.
|
||||
|
||||
# ### Generation of all the possible solutions
|
||||
#
|
||||
# #### Description
|
||||
#
|
||||
# To find the perfect solution, we can try to generate all the possible solutions and keep the best one. This way, we are sure to find the perfect solution.
|
||||
|
||||
# #### Iterative implementation
|
||||
|
||||
# In[32]:
|
||||
|
||||
|
||||
# Function to calculate all combinations of coins to make a specific amount
|
||||
def calculate_change_combinations(coins, amount):
|
||||
# Convert to integer by removing the decimal point
|
||||
amount_cents = int(amount * 100)
|
||||
coin_values_cents = [int(coin * 100) for coin in coins]
|
||||
|
||||
# Initialize a list to store combinations and their counts
|
||||
combinations = []
|
||||
list = [(0, [], 0)] # (current amount in cents, current combination, current coin index)
|
||||
|
||||
while list:
|
||||
current_amount, current_combination, current_coin_i = list.pop()
|
||||
|
||||
# If the current combination sums up to the target amount, add it to the list
|
||||
if current_amount == amount_cents:
|
||||
combinations.append(current_combination)
|
||||
|
||||
# If the amount is less than the target and that we have coins left to explore
|
||||
elif current_amount < amount_cents and current_coin_i < len(coin_values_cents):
|
||||
coin = coin_values_cents[current_coin_i]
|
||||
max_count = (amount_cents - current_amount) // coin # Maximum count of the current coin
|
||||
|
||||
# Try adding different counts of the current coin to explore possibilities
|
||||
for count in range(max_count + 1):
|
||||
new_amount = current_amount + count * coin
|
||||
new_combination = current_combination + [coins[current_coin_i]] * count
|
||||
# Push the new state onto the stack for further exploration
|
||||
list.append((new_amount, new_combination, current_coin_i + 1))
|
||||
|
||||
return combinations
|
||||
|
||||
# Tests
|
||||
|
||||
print(" Exemple 1 ")
|
||||
|
||||
coin_list = [5, 2, 1, 0.5, 0.2, 0.1, 0.05]
|
||||
print("Available coins:", coin_list)
|
||||
change_amount = 12.35
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
# Call the function to calculate and display the combinations of coins for the given amount
|
||||
start_ex1= time.perf_counter()
|
||||
sol = calculate_change_combinations(coin_list, change_amount)
|
||||
time_ex1 = start_ex1 - time.perf_counter()
|
||||
|
||||
# Find the minimum number of coins required to make the change
|
||||
min_coins = min([len(combination) for combination in sol])
|
||||
# Print the combinations with the minimum number of coins
|
||||
print(f"Minimum number of coins required: {min_coins}")
|
||||
print("Combinations:")
|
||||
for combination in sol:
|
||||
if len(combination) == min_coins:
|
||||
print(combination)
|
||||
|
||||
print("")
|
||||
print(" Exemple 2 ")
|
||||
|
||||
coin_list = [5, 2, 1.5]
|
||||
change_amount = 8
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
# Call the function to calculate and display the combinations of coins for the given amount
|
||||
start_ex2 = time.perf_counter()
|
||||
sol = calculate_change_combinations(coin_list, change_amount)
|
||||
time_ex2 = start_ex1 - time.perf_counter()
|
||||
|
||||
# Find the minimum number of coins required to make the change
|
||||
min_coins = min([len(combination) for combination in sol])
|
||||
# Print the combinations with the minimum number of coins
|
||||
print(f"Minimum number of coins required: {min_coins}")
|
||||
print("Combinations:")
|
||||
for combination in sol:
|
||||
if len(combination) == min_coins:
|
||||
print(combination)
|
||||
|
||||
print("")
|
||||
print(" Times ")
|
||||
# Print times
|
||||
print(f'Time ex1: {time_ex1:.8f}s')
|
||||
print(f'Time ex2: {time_ex2:.8f}s')
|
||||
|
||||
|
||||
# #### Recursive Implementation
|
||||
|
||||
# In[33]:
|
||||
|
||||
|
||||
def generates_all_combinations(amount, available_coins, max_coins):
|
||||
# Convert the amount to cents (an integer)
|
||||
amount_cents = int(amount * 100)
|
||||
|
||||
# Initialize a list to store all combinations
|
||||
all_combinations = []
|
||||
|
||||
coins_list = []
|
||||
for i in range(len(available_coins)): # Iterate through available coin types
|
||||
for j in range(max_coins[i]): # Repeat each coin type based on max allowed
|
||||
coins_list.append(int(available_coins[i] * 100)) # Convert coin values to cents
|
||||
|
||||
# Generate all combinations
|
||||
for r in range(1, amount_cents + 1):
|
||||
combinations_r = generate_combinations(coins_list, r, amount_cents)
|
||||
if combinations_r:
|
||||
# Convert combinations back to euros and cents
|
||||
combinations_r_euros = [c / 100 for c in combinations_r[0]]
|
||||
return combinations_r_euros
|
||||
|
||||
return all_combinations
|
||||
|
||||
# Helper
|
||||
def generate_combinations(input_list, r, target_amount, current_combination=[]):
|
||||
if r == 0:
|
||||
if sum(current_combination) == target_amount:
|
||||
return [current_combination] # Return the valid combination
|
||||
else:
|
||||
return [] # Return an empty list for invalid combinations
|
||||
|
||||
if not input_list:
|
||||
return [] # Base case: Return an empty list if input_list is empty
|
||||
|
||||
first, rest = input_list[0], input_list[1:]
|
||||
|
||||
# Generate combinations with the first element included
|
||||
with_first = generate_combinations(rest, r - 1, target_amount, current_combination + [first])
|
||||
|
||||
# Generate combinations without the first element
|
||||
without_first = generate_combinations(rest, r, target_amount, current_combination)
|
||||
|
||||
return with_first + without_first
|
||||
|
||||
# Tests
|
||||
|
||||
print(" Exemple 1 ")
|
||||
|
||||
available_coins = [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01]
|
||||
max_coins = [2, 3, 4, 2, 3, 4, 2, 3, 4]
|
||||
amount = 12.35
|
||||
|
||||
start_with_cut = time.perf_counter()
|
||||
combinations = generates_all_combinations(amount, available_coins, max_coins)
|
||||
time_with_cut = time.perf_counter() - start_with_cut
|
||||
|
||||
print("Available coins:", available_coins, "with max coins:", max_coins, "for", amount, "€")
|
||||
# Print the first valid combination found
|
||||
if combinations:
|
||||
print("Valid combination:", combinations)
|
||||
else:
|
||||
print("No valid combination found.")
|
||||
|
||||
print(" Exemple 2 ")
|
||||
|
||||
available_coins = [5, 2, 1.5]
|
||||
max_coins = [10, 10, 10]
|
||||
amount = 8
|
||||
|
||||
print("Available coins:", available_coins, "with max coins:", max_coins, "for", amount, "€")
|
||||
|
||||
start_ex2_with_cut = time.perf_counter()
|
||||
combinations = generates_all_combinations(amount, available_coins, max_coins)
|
||||
time_ex2_with_cut = time.perf_counter() - start_ex2_with_cut
|
||||
|
||||
# Print the first valid combination found
|
||||
if combinations:
|
||||
print("Valid combination:", combinations)
|
||||
else:
|
||||
print("No valid combination found.")
|
||||
|
||||
# Print times
|
||||
print("")
|
||||
print(f'Time ex1: {time_with_cut:.8f}s')
|
||||
print(f'Time ex2: {time_ex2_with_cut:.8f}s')
|
||||
|
||||
|
||||
# #### Analysis
|
||||
#
|
||||
# We manage to obtain a the perfect solution in both our implementation. As we have to generate all the combinations this takes a lot of time compare to the greedy algorithm. The time needed to compute the solutions will increase with the number of coins and the amount to change, therefore it is not a good solution for a real world problem as it will not scale.
|
||||
|
||||
# ### Dynamic Programming
|
||||
#
|
||||
# #### Description
|
||||
#
|
||||
# Dynamic programming is a common approach where we find combinations of smaller values to reach a target value. In this case, we're trying to make change using different types of coins. We consider all possible combinations of coins to reach the desired amount. This approach takes O(nW) steps, where n is the number of coin types.
|
||||
#
|
||||
# It uses a matrix to store solutions to sub-problems and returns the minimum number of coins needed to make change. If it's not possible to make change with the given coins, it returns "Infinity." Another matrix can be used to find the specific coins for the optimal solution.
|
||||
#
|
||||
# [Wikipedia](https://en.wikipedia.org/wiki/Change-making_problem)
|
||||
|
||||
# In[34]:
|
||||
|
||||
|
||||
def _get_change_making_matrix(coins, target_amount):
|
||||
# Initialize the matrix
|
||||
num_coins = len(coins)
|
||||
dp_matrix = [[0 for _ in range(target_amount + 1)] for _ in range(num_coins + 1)]
|
||||
|
||||
for i in range(target_amount + 1):
|
||||
dp_matrix[0][i] = float('inf') # By default, there is no way of making change
|
||||
|
||||
return dp_matrix
|
||||
|
||||
def find_min_coins(coins, target_amount):
|
||||
# to int :
|
||||
coins = [int(coin * 100) for coin in coins]
|
||||
target_amount = int(target_amount * 100)
|
||||
|
||||
dp_matrix = _get_change_making_matrix(coins, target_amount)
|
||||
|
||||
for c in range(1, len(coins) + 1):
|
||||
for amount in range(1, target_amount + 1):
|
||||
coin_value = coins[c - 1]
|
||||
|
||||
if coin_value == amount:
|
||||
dp_matrix[c][amount] = 1
|
||||
elif coin_value > amount:
|
||||
dp_matrix[c][amount] = dp_matrix[c - 1][amount]
|
||||
else:
|
||||
without_this_coin = dp_matrix[c - 1][amount]
|
||||
with_this_coin = 1 + dp_matrix[c][amount - coin_value]
|
||||
if with_this_coin < without_this_coin:
|
||||
dp_matrix[c][amount] = with_this_coin
|
||||
else:
|
||||
dp_matrix[c][amount] = without_this_coin
|
||||
|
||||
# Initialize a list to store the coin combinations
|
||||
coin_combinations = []
|
||||
|
||||
# Backtrack to find the coin combinations
|
||||
c, r = len(coins), target_amount
|
||||
while c > 0 and r > 0:
|
||||
if dp_matrix[c][r] == dp_matrix[c - 1][r]:
|
||||
c -= 1
|
||||
else:
|
||||
coin_combinations.append(coins[c - 1])
|
||||
r -= coins[c - 1]
|
||||
|
||||
# Divide the coin values by 100 to convert back to euros
|
||||
coin_combinations = [coin / 100 for coin in coin_combinations]
|
||||
return coin_combinations
|
||||
|
||||
|
||||
# Tests
|
||||
|
||||
print(" Exemple 1 ")
|
||||
|
||||
available_coins = [5, 0.5, 0.1, 2, 1, 0.2, 0.05, 0.02, 0.01]
|
||||
amount = 12.35
|
||||
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
start_ex1 = time.perf_counter()
|
||||
result = find_min_coins(available_coins, amount)
|
||||
time_ex1 = time.perf_counter() - start_ex1
|
||||
|
||||
if result is not None:
|
||||
print(f"Minimum number of coins needed: {len(result)}")
|
||||
print(f"Coin combinations: {result}")
|
||||
else:
|
||||
print("It's not possible to make change for the given amount.")
|
||||
|
||||
|
||||
print(" Exemple 2 ")
|
||||
|
||||
available_coins = [5, 2, 1.5]
|
||||
amount = 8
|
||||
|
||||
print("Available coins:", available_coins, "for", amount, "€")
|
||||
|
||||
start_ex2 = time.perf_counter()
|
||||
result = find_min_coins(available_coins, amount)
|
||||
time_ex2 = time.perf_counter() - start_ex2
|
||||
|
||||
|
||||
if result is not None:
|
||||
print(f"Minimum number of coins needed: {len(result)}")
|
||||
print(f"Coin combinations: {result}")
|
||||
else:
|
||||
print("It's not possible to make change for the given amount.")
|
||||
|
||||
# Times
|
||||
|
||||
print("")
|
||||
print(f'Time ex1: {time_ex1:.8f}s')
|
||||
print(f'Time ex2: {time_ex2:.8f}s')
|
||||
|
||||
|
||||
# #### Analysis
|
||||
#
|
||||
# Using this method, we are able to generate a perfect solution in a reasonable amount of time. It's faster than generating all the possible solutions.
|
||||
2
TD2-8_Make_ChangePrograms/AllTheSolutionsTest1.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
[[5, 5, 2, 0.2, 0.1, 0.05]]
|
||||
Previous Program 3 execution time: 0.40930659999139607
|
||||
Modified Program 3 execution time: 0.580361800006358
|
||||
BIN
TD2-8_Make_ChangePrograms/TP2.zip
Normal file
BIN
TD2-8_Make_ChangePrograms/__pycache__/prg4.cpython-311.pyc
Normal file
33
TD2-8_Make_ChangePrograms/prg1.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# gm
|
||||
import time
|
||||
|
||||
# Greedy : function to make change with sorted coins
|
||||
def greedy_make_change(amount, coins):
|
||||
i = 0
|
||||
change = []
|
||||
|
||||
while amount > 0 and len(coins) > i:
|
||||
# print(str(round(amount//coins[i])) + " Coins of " + str(coins[i]) + "€")
|
||||
for j in range(round(amount//coins[i])):
|
||||
change.append(coins[i])
|
||||
amount = round(amount%coins[i], 2)
|
||||
i = i+1
|
||||
if amount > 0:
|
||||
print(f"Cannot make exact change for {amount:.2f}€")
|
||||
return change
|
||||
|
||||
|
||||
# Initialisation
|
||||
L = [5,2,1,0.5,0.2,0.1,0.05]
|
||||
A = 12.35
|
||||
|
||||
start_time = time.perf_counter()
|
||||
solution = greedy_make_change(A, L)
|
||||
total_time = time.perf_counter() - start_time
|
||||
|
||||
# Print to file
|
||||
with open("sol_1.txt", "w") as f:
|
||||
f.write(str(solution))
|
||||
f.write("\n")
|
||||
f.write(str(total_time))
|
||||
|
||||
61
TD2-8_Make_ChangePrograms/prg2.py
Normal file
@@ -0,0 +1,61 @@
|
||||
# aw
|
||||
import time
|
||||
|
||||
# Function to count occurrences of items in a list
|
||||
def count_occurrences(array):
|
||||
counts = {}
|
||||
for item in array:
|
||||
if item in counts:
|
||||
counts[item] += 1 # If the item is already in the dictionary, increment its count
|
||||
else:
|
||||
counts[item] = 1 # If the item is not in the dictionary, add it with a count of 1
|
||||
return counts
|
||||
|
||||
# Function to calculate all combinations of coins to make a specific amount
|
||||
def calculate_change_combinations(coins, amount):
|
||||
# Convert euro amounts to cents for calculations
|
||||
amount_cents = int(amount * 100)
|
||||
coin_values_cents = [int(coin * 100) for coin in coins]
|
||||
|
||||
# Initialize a list to store combinations and their counts
|
||||
combinations = []
|
||||
stack = [(0, [], 0)] # (current amount in cents, current combination, current coin index)
|
||||
|
||||
while stack:
|
||||
current_amount, current_combination, current_coin_index = stack.pop()
|
||||
|
||||
# If the current combination sums up to the target amount, add it to the list
|
||||
if current_amount == amount_cents:
|
||||
combinations.append(current_combination)
|
||||
# If the current amount is less than the target amount and there are more coins to consider
|
||||
elif current_amount < amount_cents and current_coin_index < len(coin_values_cents):
|
||||
coin = coin_values_cents[current_coin_index]
|
||||
max_count = (amount_cents - current_amount) // coin # Maximum count of the current coin
|
||||
|
||||
# Try adding different counts of the current coin to explore possibilities
|
||||
for count in range(max_count + 1):
|
||||
new_amount = current_amount + count * coin
|
||||
new_combination = current_combination + [coins[current_coin_index]] * count
|
||||
# Push the new state onto the stack for further exploration
|
||||
stack.append((new_amount, new_combination, current_coin_index + 1))
|
||||
|
||||
# Print the total number of combinations
|
||||
print(f"Total number of combinations: {len(combinations)}")
|
||||
return combinations
|
||||
|
||||
# Define the list of available coin denominations and the amount amount of change
|
||||
coin_list = [5, 2, 1, 0.5, 0.2, 0.1, 0.05]
|
||||
print("The types of coins we have are:", coin_list)
|
||||
change_amount = 12.35
|
||||
print("The amount of change we need to return is:", change_amount)
|
||||
|
||||
start_time = time.perf_counter()
|
||||
# Call the function to calculate and display the combinations of coins for the given amount
|
||||
combinations = calculate_change_combinations(coin_list, change_amount)
|
||||
total_time = time.perf_counter() - start_time
|
||||
|
||||
# Write the solution to a .txt file
|
||||
with open("sol_2.txt", "w") as f:
|
||||
f.write(str(combinations))
|
||||
f.write("\n\n")
|
||||
f.write("Execution time is: " + str(total_time))
|
||||
40
TD2-8_Make_ChangePrograms/prg3.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# km
|
||||
import math
|
||||
import time
|
||||
|
||||
def make_change_recursive(coins, amount, start, current_change, result):
|
||||
if amount == 0:
|
||||
result.append(current_change[:])
|
||||
return
|
||||
|
||||
for i in range(start, len(coins)):
|
||||
coin_cents = round(coins[i] * 100)
|
||||
if amount >= coin_cents:
|
||||
current_change.append(coins[i])
|
||||
make_change_recursive(coins, amount - coin_cents, i, current_change, result)
|
||||
current_change.pop()
|
||||
|
||||
return result
|
||||
|
||||
def main():
|
||||
coins = [5, 2, 0.2, 0.1, 0.05] # Available coin values
|
||||
target_amount = 12.35 # Change to be made
|
||||
solutions = [] # Store all valid combinations
|
||||
|
||||
start_time = time.perf_counter()
|
||||
solutions = make_change_recursive(coins, target_amount * 100, 0, [], [])
|
||||
end_time = time.perf_counter()
|
||||
|
||||
duration = end_time - start_time
|
||||
|
||||
with open("AllTheSolutionsTest1.txt", "w") as file:
|
||||
# file.write("execution time: "+str(duration)+"s\n")
|
||||
file.write(str(solutions))
|
||||
# for solution in solutions:
|
||||
# file.write(",".join(map(str, solution)) + "\n")
|
||||
# print(solution,"\n")
|
||||
|
||||
file.write("\n"+str(duration)+"\n")
|
||||
print("execution time: "+str(duration)+"s")
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
44
TD2-8_Make_ChangePrograms/prg3_bis.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# km
|
||||
import math
|
||||
import time
|
||||
|
||||
def make_change_recursive(coins, amount, start, current_change, result):
|
||||
if amount == 0:
|
||||
result.append(current_change[:])
|
||||
return
|
||||
|
||||
for i in range(start, len(coins)):
|
||||
coin_cents = round(coins[i] * 100)
|
||||
if amount >= coin_cents:
|
||||
current_change.append(coins[i])
|
||||
make_change_recursive(coins, amount - coin_cents, i, current_change, result)
|
||||
current_change.pop()
|
||||
|
||||
return result
|
||||
|
||||
def main():
|
||||
coins = [5, 2, 0.2, 0.1, 0.05] # Available coin values
|
||||
target_amount = 12.35 # Change to be made
|
||||
solutions = [] # Store all valid combinations
|
||||
|
||||
# Sort the coins in descending order,
|
||||
# so that the highest values are used first
|
||||
coins.sort(reverse=True)
|
||||
|
||||
start_time = time.perf_counter()
|
||||
solutions = make_change_recursive(coins, target_amount * 100, 0, [], [])
|
||||
end_time = time.perf_counter()
|
||||
|
||||
duration = end_time - start_time
|
||||
|
||||
with open("AllTheSolutionsTest1.txt", "w") as file:
|
||||
# file.write("execution time: "+str(duration)+"s\n")
|
||||
file.write(str(solutions))
|
||||
# for solution in solutions:
|
||||
# file.write(",".join(map(str, solution)) + "\n")
|
||||
# print(solution,"\n")
|
||||
|
||||
file.write("\n"+str(duration)+"\n")
|
||||
print("execution time: "+str(duration)+"s")
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
80
TD2-8_Make_ChangePrograms/prg4.py
Normal file
@@ -0,0 +1,80 @@
|
||||
# gm
|
||||
|
||||
# You calculate all the solutions that you do not display, as you go.
|
||||
# If you store the solutions in a 2D array of solutions, in the order in which
|
||||
# the valid combinations are found, then a subsequent display following
|
||||
# that order can be compared with the contents of the CORRECT_ ... file
|
||||
# and will show no difference.
|
||||
|
||||
import time
|
||||
|
||||
# Function to calculate all combinations of coins to make a specific amount
|
||||
def calculate_change_combinations(coins, amount):
|
||||
# Convert euro amounts to cents for calculations
|
||||
amount_cents = int(amount * 100)
|
||||
coin_values_cents = [int(coin * 100) for coin in coins]
|
||||
|
||||
# Initialize a list to store combinations and their counts
|
||||
combinations = []
|
||||
stack = [(0, [], 0)] # (current amount in cents, current combination, current coin index)
|
||||
|
||||
while stack:
|
||||
current_amount, current_combination, current_coin_index = stack.pop()
|
||||
|
||||
# If the current combination sums up to the target amount, add it to the list
|
||||
if current_amount == amount_cents:
|
||||
combinations.append(current_combination)
|
||||
|
||||
# If the current amount is less than the target amount and there are more coins to consider
|
||||
elif current_amount < amount_cents and current_coin_index < len(coin_values_cents):
|
||||
coin = coin_values_cents[current_coin_index]
|
||||
max_count = (amount_cents - current_amount) // coin # Maximum count of the current coin
|
||||
|
||||
# Try adding different counts of the current coin to explore possibilities
|
||||
for count in range(max_count + 1):
|
||||
new_amount = current_amount + count * coin
|
||||
new_combination = current_combination + [coins[current_coin_index]] * count
|
||||
# Push the new state onto the stack for further exploration
|
||||
stack.append((new_amount, new_combination, current_coin_index + 1))
|
||||
|
||||
return combinations
|
||||
|
||||
# Define the list of available coin denominations and the amount amount of change
|
||||
L = [5, 2, 1, 0.5, 0.2, 0.1, 0.05]
|
||||
A = 12.35
|
||||
|
||||
start = time.perf_counter()
|
||||
solution = calculate_change_combinations(L, A)
|
||||
total_time = time.perf_counter() - start
|
||||
|
||||
with open("sol_4.txt", "w") as f:
|
||||
f.write(str(solution))
|
||||
f.write("\n")
|
||||
f.write(str(total_time))
|
||||
|
||||
|
||||
# Read CORRECT_ ... file and compare with the contents of the list of solutions
|
||||
# Do not consider the last line
|
||||
with open("AllTheSolutionsTest1.txt", "r") as f:
|
||||
correct_solution = f.read().splitlines()[:-1]
|
||||
|
||||
|
||||
# Sort each array on correct_solution and solution
|
||||
for i in range(len(solution)):
|
||||
solution[i].sort()
|
||||
|
||||
for i in range(len(correct_solution)):
|
||||
correct_solution[i] = correct_solution[i].replace("[", "").replace("]", "").replace(",", "").split()
|
||||
correct_solution[i].sort()
|
||||
|
||||
# Check if each element of correct_solution is in solution
|
||||
missing = 0
|
||||
missing_solution = []
|
||||
for i in range(len(correct_solution)):
|
||||
if correct_solution[i] not in solution:
|
||||
print("ERROR: solution not found")
|
||||
missing +=1
|
||||
missing_solution.append(correct_solution[i])
|
||||
|
||||
print("Number of missing solutions: ", missing)
|
||||
# print("Missing solutions: ", missing_solution)
|
||||
81
TD2-8_Make_ChangePrograms/prg5.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# km
|
||||
|
||||
# modify program 3 so as to evaluate the cost of a solution
|
||||
# according to the number of units of the values. Calculate the best
|
||||
# solution according to this cost by displaying successive values that
|
||||
# improve. Display the trace of this execution in a file
|
||||
# CORRECT_SolutionsWhichImproveIncrementally.txt
|
||||
# Calculate the number of solutions displayed, and the display economy,
|
||||
# using the wc –l command. Calculate with the time, or date command,
|
||||
# the execution time programs 3 and 5
|
||||
|
||||
# wc -l command ?
|
||||
|
||||
import math
|
||||
import time
|
||||
|
||||
def make_change_recursive_prg3(coins, amount, start, current_change, result):
|
||||
if amount == 0:
|
||||
result.append(current_change[:])
|
||||
return
|
||||
|
||||
for i in range(start, len(coins)):
|
||||
coin_cents = round(coins[i] * 100)
|
||||
if amount >= coin_cents:
|
||||
current_change.append(coins[i])
|
||||
make_change_recursive_prg3(coins, amount - coin_cents, i, current_change, result)
|
||||
current_change.pop()
|
||||
|
||||
return result
|
||||
|
||||
def calculate_solution_cost(solution):
|
||||
# Calculate the cost of a solution as the sum of units of values
|
||||
cost = sum([round(coin * 100) for coin in solution])
|
||||
return cost
|
||||
|
||||
def make_change_recursive(coins, amount, start, current_change, result, best_cost):
|
||||
if amount == 0:
|
||||
current_cost = calculate_solution_cost(current_change)
|
||||
if current_cost < best_cost[0]:
|
||||
result.append(current_change[:])
|
||||
best_cost[0] = current_cost
|
||||
return
|
||||
|
||||
for i in range(start, len(coins)):
|
||||
coin_cents = round(coins[i] * 100)
|
||||
if amount >= coin_cents:
|
||||
current_change.append(coins[i])
|
||||
make_change_recursive(coins, amount - coin_cents, i, current_change, result, best_cost)
|
||||
current_change.pop()
|
||||
|
||||
def main():
|
||||
coins = [5, 2, 0.2, 0.1, 0.05] # Available coin values
|
||||
target_amount = 12.35 # Change to be made
|
||||
solutions = [] # Store all valid combinations
|
||||
best_cost = [float('inf')] # Initialize with a very high cost
|
||||
|
||||
start_time_prg3 = time.perf_counter()
|
||||
solutions = make_change_recursive_prg3(coins, target_amount * 100, 0, [], [])
|
||||
end_time_prg3 = time.perf_counter()
|
||||
duration_prg3 = end_time_prg3 - start_time_prg3
|
||||
|
||||
solutions = []
|
||||
start_time = time.perf_counter()
|
||||
make_change_recursive(coins, target_amount * 100, 0, [], solutions, best_cost)
|
||||
end_time = time.perf_counter()
|
||||
|
||||
duration = end_time - start_time
|
||||
|
||||
with open("CORRECT_SolutionsWhichImproveIncrementally.txt", "w") as file:
|
||||
file.write(str(solutions))
|
||||
file.write("\nPrevious Program 3 execution time: " + str(duration_prg3))
|
||||
file.write("\nModified Program 3 execution time: " + str(duration))
|
||||
# for solution in solutions:
|
||||
# file.write(",".join(map(str, solution)) + "\n")
|
||||
|
||||
print("Previous Program 3 execution time: " + str(duration_prg3) + "\n")
|
||||
print("Modified Program 3 execution time: " + str(duration) + "\n")
|
||||
print("Total solutions displayed:", len(solutions))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
64
TD2-8_Make_ChangePrograms/prg6.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# modify program 5 or program 4 so that the best calculated
|
||||
# solution is stored in a solution table. Evaluate the computation time
|
||||
# saved and the display savings.
|
||||
|
||||
import time
|
||||
|
||||
# Function to calculate all combinations of coins to make a specific amount
|
||||
def calculate_change_combinations(coins, amount):
|
||||
# Convert euro amounts to cents for calculations
|
||||
amount_cents = int(amount * 100)
|
||||
coin_values_cents = [int(coin * 100) for coin in coins]
|
||||
|
||||
# Initialize a list to store combinations and their counts
|
||||
combinations = []
|
||||
stack = [(0, [], 0)] # (current amount in cents, current combination, current coin index)
|
||||
|
||||
best_solution = [[] for i in range(len(coins))]
|
||||
|
||||
while stack:
|
||||
current_amount, current_combination, current_coin_index = stack.pop()
|
||||
|
||||
# If the current combination sums up to the target amount, add it to the list
|
||||
if current_amount == amount_cents:
|
||||
if len(current_combination) < len(best_solution):
|
||||
best_solution = current_combination
|
||||
|
||||
# If the current amount is less than the target amount and there are more coins to consider
|
||||
elif current_amount < amount_cents and current_coin_index < len(coin_values_cents):
|
||||
coin = coin_values_cents[current_coin_index]
|
||||
max_count = (amount_cents - current_amount) // coin # Maximum count of the current coin
|
||||
|
||||
# Try adding different counts of the current coin to explore possibilities
|
||||
for count in range(max_count + 1):
|
||||
new_amount = current_amount + count * coin
|
||||
new_combination = current_combination + [coins[current_coin_index]] * count
|
||||
# Push the new state onto the stack for further exploration
|
||||
stack.append((new_amount, new_combination, current_coin_index + 1))
|
||||
|
||||
return best_solution
|
||||
|
||||
# Define the list of available coin denominations and the amount amount of change
|
||||
L = [5, 2, 1, 0.5, 0.2, 0.1, 0.05]
|
||||
A = 12.35
|
||||
|
||||
start = time.perf_counter()
|
||||
solution = calculate_change_combinations(L, A)
|
||||
|
||||
with open("sol_4-6.txt", "w") as f:
|
||||
f.write(str(solution))
|
||||
f.write("\n")
|
||||
|
||||
total_time_3 = time.perf_counter() - start
|
||||
|
||||
start = time.perf_counter()
|
||||
solution = calculate_change_combinations(L, A)
|
||||
total_time_6 = time.perf_counter() - start
|
||||
|
||||
print("Solution: ", solution)
|
||||
print("Total time for program 3: ", total_time_3)
|
||||
print("Total time for program 6: ", total_time_6)
|
||||
|
||||
|
||||
|
||||
|
||||
68
TD2-8_Make_ChangePrograms/prg7.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# modify program 5 or program 4 so that the best calculated
|
||||
# solution is stored in a solution table. Evaluate the computation time
|
||||
# saved and the display savings.
|
||||
|
||||
import time
|
||||
|
||||
# Function to calculate all combinations of coins to make a specific amount
|
||||
def calculate_change_combinations(coins, amount):
|
||||
# Convert euro amounts to cents for calculations
|
||||
amount_cents = int(amount * 100)
|
||||
coin_values_cents = [int(coin * 100) for coin in coins]
|
||||
|
||||
# Initialize a list to store combinations and their counts
|
||||
combinations = []
|
||||
stack = [(0, [], 0)] # (current amount in cents, current combination, current coin index)
|
||||
|
||||
best_solution = [[] for i in range(100)]
|
||||
|
||||
increment = 0
|
||||
|
||||
while stack:
|
||||
current_amount, current_combination, current_coin_index = stack.pop()
|
||||
|
||||
# If the current combination sums up to the target amount, add it to the list
|
||||
if current_amount == amount_cents:
|
||||
if len(current_combination) < len(best_solution):
|
||||
best_solution = current_combination
|
||||
else:
|
||||
increment += 1
|
||||
|
||||
# If we don't improve our result 5 times in a row, we cut
|
||||
if (increment >= 5):
|
||||
return best_solution
|
||||
|
||||
# If the current amount is less than the target amount and there are more coins to consider
|
||||
elif current_amount < amount_cents and current_coin_index < len(coin_values_cents):
|
||||
coin = coin_values_cents[current_coin_index]
|
||||
max_count = (amount_cents - current_amount) // coin # Maximum count of the current coin
|
||||
|
||||
# Try adding different counts of the current coin to explore possibilities
|
||||
for count in range(max_count + 1):
|
||||
new_amount = current_amount + count * coin
|
||||
new_combination = current_combination + [coins[current_coin_index]] * count
|
||||
# Push the new state onto the stack for further exploration
|
||||
stack.append((new_amount, new_combination, current_coin_index + 1))
|
||||
|
||||
return best_solution
|
||||
|
||||
# Define the list of available coin denominations and the amount amount of change
|
||||
L = [5, 2, 1, 0.5, 0.2, 0.1, 0.05]
|
||||
A = 12.35
|
||||
|
||||
start = time.perf_counter()
|
||||
solution = calculate_change_combinations(L, A)
|
||||
|
||||
with open("sol_4-7.txt", "w") as f:
|
||||
f.write(str(solution))
|
||||
f.write("\n")
|
||||
|
||||
total_time_3 = time.perf_counter() - start
|
||||
|
||||
start = time.perf_counter()
|
||||
solution = calculate_change_combinations(L, A)
|
||||
total_time_7 = time.perf_counter() - start
|
||||
|
||||
print("Solution: ", solution)
|
||||
print("Total time for program 3: ", total_time_3)
|
||||
print("Total time for program 7: ", total_time_7)
|
||||
2
TD2-8_Make_ChangePrograms/sol_1.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
[5, 5, 2, 0.2, 0.1, 0.05]
|
||||
2.6099999999997653e-05
|
||||
3
TD2-8_Make_ChangePrograms/sol_2.txt
Normal file
2
TD2-8_Make_ChangePrograms/sol_3.txt
Normal file
2
TD2-8_Make_ChangePrograms/sol_3_bis.txt
Normal file
1
TD2-8_Make_ChangePrograms/sol_4-6.txt
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
1
TD2-8_Make_ChangePrograms/sol_4-7.txt
Normal file
@@ -0,0 +1 @@
|
||||
[5, 5, 2, 0.2, 0.1, 0.05]
|
||||
2
TD2-8_Make_ChangePrograms/sol_4.txt
Normal file
3
TD2-8_Make_ChangePrograms/sol_5.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
[[5, 5, 2, 0.2, 0.1, 0.05]]
|
||||
Previous Program 3 execution time: 0.4225550999981351
|
||||
Modified Program 3 execution time: 0.5911691000073915
|
||||
488
TD3b/.ipynb_checkpoints/TD3-checkpoint.ipynb
Normal file
@@ -0,0 +1,488 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 78,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# matrices from the document\n",
|
||||
"R1 = [[1,0,1],[0,1,0],[1,0,0]]\n",
|
||||
"R2 = [[1,1,0],[0,0,1],[1,0,1]]\n",
|
||||
"R3 = [[1, 1, 1], [0, 0, 1], [0, 0, 1]]\n",
|
||||
"R4 = [[1, 1, 0, 1], [1, 1, 0, 1], [0, 0, 1, 0], [1, 1, 0, 1]]\n",
|
||||
"R5 = [[0, 1, 0], [0, 0, 1], [1, 0, 0]]\n",
|
||||
"R6 = [[1, 1, 1], [0, 0, 1], [1, 0, 0]]\n",
|
||||
"\n",
|
||||
"tests = [R1, R2, R3, R4, R5, R6]\n",
|
||||
"\n",
|
||||
"CHAMPIONS = [[1,1,0,1,1],\n",
|
||||
" [0,1,0,0,0],\n",
|
||||
" [1,1,1,1,1],\n",
|
||||
" [1,1,0,1,1],\n",
|
||||
" [0,1,0,0,1]]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 79,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Check if a matrix is reflexive\n",
|
||||
"def reflexive(R):\n",
|
||||
" for i in range(len(R)):\n",
|
||||
" if R[i][i] != 1:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"# Check if a matrix is symmetric\n",
|
||||
"def symmetrical(R):\n",
|
||||
" for i in range(len(R)):\n",
|
||||
" for j in range(len(R)):\n",
|
||||
" if R[i][j] != R[j][i]:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"# Check if a matrix is anti-symmetric\n",
|
||||
"def anti_symmetrical(R):\n",
|
||||
" for i in range(len(R)):\n",
|
||||
" for j in range(len(R)):\n",
|
||||
" if R[i][j] > 0 and R[j][i] > 0 and i != j:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"# Check if a matrix is asymmetrical\n",
|
||||
"def asymmetrical(R):\n",
|
||||
" for i in range(len(R)):\n",
|
||||
" for j in range(len(R)):\n",
|
||||
" if R[i][j] > 0 and R[j][i] > 0:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"# Check if a matrix is irreflexive\n",
|
||||
"def irreflexive(R):\n",
|
||||
" for i in range(len(R)):\n",
|
||||
" if R[i][i] > 0:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"# Check if a matrix is transitive\n",
|
||||
"def transitive(matrix):\n",
|
||||
" is_transitive = True\n",
|
||||
" for i in range(len(matrix)):\n",
|
||||
" for j in range(len(matrix)):\n",
|
||||
" if matrix[i][j] > 0:\n",
|
||||
" for k in range(len(matrix)):\n",
|
||||
" if matrix[j][k] > 0:\n",
|
||||
" is_transitive = is_transitive and matrix[i][k] > 0\n",
|
||||
"\n",
|
||||
" return is_transitive\n",
|
||||
"\n",
|
||||
"# Check if a matrix is intransitive\n",
|
||||
"def intransitive(matrix):\n",
|
||||
" is_transitive = True\n",
|
||||
" for i in range(len(matrix)):\n",
|
||||
" for j in range(len(matrix)):\n",
|
||||
" if matrix[i][j] > 0:\n",
|
||||
" for k in range(len(matrix)):\n",
|
||||
" if matrix[j][k] > 0:\n",
|
||||
" is_transitive = is_transitive and matrix[i][k] != 1\n",
|
||||
"\n",
|
||||
" return is_transitive\n",
|
||||
"\n",
|
||||
"# Check if a matrix is non-transitive\n",
|
||||
"def non_transitive(matrix):\n",
|
||||
" n = len(matrix)\n",
|
||||
"\n",
|
||||
" for i in range(n):\n",
|
||||
" for j in range(n):\n",
|
||||
" if matrix[i][j] > 0:\n",
|
||||
" for k in range(n):\n",
|
||||
" if matrix[j][k] > 0 and matrix[i][k] == 0:\n",
|
||||
" return True\n",
|
||||
" return False"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 80,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 1\n",
|
||||
"reflexive: False\n",
|
||||
"irreflexive: False\n",
|
||||
"symmetrical: True\n",
|
||||
"anti_symmetrical: False\n",
|
||||
"asymmetrical: False\n",
|
||||
"transitive: False\n",
|
||||
"intransitive: False\n",
|
||||
"non_transitive: True\n",
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 2\n",
|
||||
"reflexive: False\n",
|
||||
"irreflexive: False\n",
|
||||
"symmetrical: False\n",
|
||||
"anti_symmetrical: True\n",
|
||||
"asymmetrical: False\n",
|
||||
"transitive: False\n",
|
||||
"intransitive: False\n",
|
||||
"non_transitive: True\n",
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 3\n",
|
||||
"reflexive: False\n",
|
||||
"irreflexive: False\n",
|
||||
"symmetrical: False\n",
|
||||
"anti_symmetrical: True\n",
|
||||
"asymmetrical: False\n",
|
||||
"transitive: True\n",
|
||||
"intransitive: False\n",
|
||||
"non_transitive: False\n",
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 4\n",
|
||||
"reflexive: True\n",
|
||||
"irreflexive: False\n",
|
||||
"symmetrical: True\n",
|
||||
"anti_symmetrical: False\n",
|
||||
"asymmetrical: False\n",
|
||||
"transitive: True\n",
|
||||
"intransitive: False\n",
|
||||
"non_transitive: False\n",
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 5\n",
|
||||
"reflexive: False\n",
|
||||
"irreflexive: True\n",
|
||||
"symmetrical: False\n",
|
||||
"anti_symmetrical: True\n",
|
||||
"asymmetrical: True\n",
|
||||
"transitive: False\n",
|
||||
"intransitive: True\n",
|
||||
"non_transitive: True\n",
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 6\n",
|
||||
"reflexive: False\n",
|
||||
"irreflexive: False\n",
|
||||
"symmetrical: False\n",
|
||||
"anti_symmetrical: False\n",
|
||||
"asymmetrical: False\n",
|
||||
"transitive: False\n",
|
||||
"intransitive: False\n",
|
||||
"non_transitive: True\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Testing all the matrices from the slides\n",
|
||||
"\n",
|
||||
"for i in range(len(tests)):\n",
|
||||
" print(\"--------------------------------------------------\")\n",
|
||||
" print(\"Matrix: R\", i+1)\n",
|
||||
" a = reflexive(tests[i])\n",
|
||||
" print(\"reflexive: \", a)\n",
|
||||
" a = irreflexive(tests[i])\n",
|
||||
" print(\"irreflexive: \", a)\n",
|
||||
" a = symmetrical(tests[i])\n",
|
||||
" print(\"symmetrical: \", a)\n",
|
||||
" a = anti_symmetrical(tests[i])\n",
|
||||
" print(\"anti_symmetrical: \", a)\n",
|
||||
" a = asymmetrical(tests[i])\n",
|
||||
" print(\"asymmetrical: \", a)\n",
|
||||
" a = transitive(tests[i])\n",
|
||||
" print(\"transitive: \", a)\n",
|
||||
" a = intransitive(tests[i])\n",
|
||||
" print(\"intransitive: \", a)\n",
|
||||
" a = non_transitive(tests[i])\n",
|
||||
" print(\"non_transitive: \", a)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 81,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Equivalence classes\n",
|
||||
"\n",
|
||||
"def equivalence_classes(R):\n",
|
||||
" n = len(R)\n",
|
||||
" classes = []\n",
|
||||
" scores = []\n",
|
||||
"\n",
|
||||
" # Count number of (x,y) existing on each row\n",
|
||||
" for x in range(n):\n",
|
||||
" score = 0\n",
|
||||
" for y in range(n):\n",
|
||||
" if R[x][y] > 0:\n",
|
||||
" score += 1\n",
|
||||
" scores.append(score)\n",
|
||||
"\n",
|
||||
" lastScore = 0\n",
|
||||
"\n",
|
||||
" for i in range(n):\n",
|
||||
" max_score = 0\n",
|
||||
" max_index = 0\n",
|
||||
" for j in range(n):\n",
|
||||
" if scores[j] > max_score:\n",
|
||||
" max_score = scores[j]\n",
|
||||
" max_index = j\n",
|
||||
"\n",
|
||||
" if(max_score == lastScore):\n",
|
||||
" classes[len(classes)-1].append(max_index)\n",
|
||||
" else:\n",
|
||||
" # Add the class to the list of classes\n",
|
||||
" classes.append([max_index])\n",
|
||||
"\n",
|
||||
" lastScore = max_score\n",
|
||||
" scores[max_index] = 0\n",
|
||||
"\n",
|
||||
" return classes, scores\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 82,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"([[2], [0, 3], [4], [1]], [0, 0, 0, 0, 0])\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(equivalence_classes(CHAMPIONS))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 83,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[1, 1, 1, 1]\n",
|
||||
"[0, 0, 0, 0]\n",
|
||||
"[1, 1, 1, 1]\n",
|
||||
"[1, 1, 1, 1]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def transitive_closure(adjacency_matrix):\n",
|
||||
" \"\"\"\n",
|
||||
" Calculate the transitive closure of a directed graph represented as an adjacency matrix.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" adjacency_matrix (list of lists): The adjacency matrix of the graph.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" list of lists: The transitive closure matrix.\n",
|
||||
" \"\"\"\n",
|
||||
" n = len(adjacency_matrix)\n",
|
||||
"\n",
|
||||
" # Initialize the transitive closure matrix as a copy of the adjacency matrix\n",
|
||||
" closure_matrix = [row[:] for row in adjacency_matrix]\n",
|
||||
"\n",
|
||||
" # Perform the Floyd-Warshall algorithm to compute transitive closure\n",
|
||||
" for k in range(n):\n",
|
||||
" for i in range(n):\n",
|
||||
" for j in range(n):\n",
|
||||
" closure_matrix[i][j] = closure_matrix[i][j] or (closure_matrix[i][k] and closure_matrix[k][j])\n",
|
||||
"\n",
|
||||
" return closure_matrix\n",
|
||||
"\n",
|
||||
"# Example usage:\n",
|
||||
"adjacency_matrix = [\n",
|
||||
" [0, 1, 0, 0],\n",
|
||||
" [0, 0, 0, 1],\n",
|
||||
" [1, 0, 0, 0],\n",
|
||||
" [0, 0, 1, 0]\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"ex_cours = [\n",
|
||||
" [0, 0, 1, 0],\n",
|
||||
" [0, 0, 0, 0],\n",
|
||||
" [0, 1, 1, 1],\n",
|
||||
" [1, 0, 0, 0]\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"transitive_closure_matrix = transitive_closure(ex_cours)\n",
|
||||
"\n",
|
||||
"# Print the transitive closure matrix\n",
|
||||
"for row in transitive_closure_matrix:\n",
|
||||
" print(row)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 84,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[3, 2, 1, 2]\n",
|
||||
"[0, 0, 0, 0]\n",
|
||||
"[2, 1, 1, 1]\n",
|
||||
"[1, 3, 2, 3]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Make transitive closure\n",
|
||||
"\n",
|
||||
"transitive_closure_matrix = [\n",
|
||||
" [1, 0, 0, 0],\n",
|
||||
" [0, 1, 0, 1],\n",
|
||||
" [1, 0, 0, 0],\n",
|
||||
" [0, 0, 1, 1]\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"def transitive_closure(matrix):\n",
|
||||
" modification = True\n",
|
||||
" while modification:\n",
|
||||
" modification = False\n",
|
||||
" for i in range(len(matrix)):\n",
|
||||
" for j in range(len(matrix)):\n",
|
||||
" for k in range(len(matrix)):\n",
|
||||
" if matrix[i][j] > 0 and matrix[j][k] > 0 and matrix[i][k] == 0:\n",
|
||||
" matrix[i][k] = matrix[i][j] + matrix[j][k]\n",
|
||||
" modification = True\n",
|
||||
" else:\n",
|
||||
" modification = False\n",
|
||||
"\n",
|
||||
" return matrix\n",
|
||||
"\n",
|
||||
"full_closure = transitive_closure(ex_cours)\n",
|
||||
"\n",
|
||||
"for row in full_closure:\n",
|
||||
" print(row)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 85,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Removing 0 0\n",
|
||||
"Removing 1 1\n",
|
||||
"Removing 1 3\n",
|
||||
"Removing 3 3\n",
|
||||
"[0, 0, 0, 0]\n",
|
||||
"[0, 0, 2, 0]\n",
|
||||
"[1, 0, 0, 0]\n",
|
||||
"[2, 0, 1, 0]\n",
|
||||
"Removing 0 0\n",
|
||||
"Removing 1 1\n",
|
||||
"Removing 2 2\n",
|
||||
"Removing 3 3\n",
|
||||
"Removing 4 4\n",
|
||||
"Removing 5 5\n",
|
||||
"Removing 6 6\n",
|
||||
"Removing 0 4\n",
|
||||
"Removing 1 5\n",
|
||||
"Removing 3 5\n",
|
||||
"Removing 4 6\n",
|
||||
"Removing 0 6\n",
|
||||
"[0, 1, 1, 1, 0, 1, 0]\n",
|
||||
"[0, 0, 0, 0, 1, 0, 1]\n",
|
||||
"[0, 0, 0, 0, 0, 0, 1]\n",
|
||||
"[0, 0, 0, 0, 1, 0, 1]\n",
|
||||
"[0, 0, 0, 0, 0, 1, 0]\n",
|
||||
"[0, 0, 0, 0, 0, 0, 1]\n",
|
||||
"[0, 0, 0, 0, 0, 0, 0]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Remove transitive closure\n",
|
||||
"\n",
|
||||
"transitive_closure_matrix = [\n",
|
||||
"[1, 0, 0, 0],\n",
|
||||
"[0, 1, 2, 1],\n",
|
||||
"[1, 0, 0, 0],\n",
|
||||
"[2, 0, 1, 1]\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"m = [\n",
|
||||
" [1,1,1,1,1,1,1],\n",
|
||||
" [0,1,0,0,1,1,1],\n",
|
||||
" [0,0,1,0,0,0,1],\n",
|
||||
" [0,0,0,1,1,1,1],\n",
|
||||
" [0,0,0,0,1,1,1],\n",
|
||||
" [0,0,0,0,0,1,1],\n",
|
||||
" [0,0,0,0,0,0,1]\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"def remove_transitive_closure(matrix):\n",
|
||||
" n = len(matrix)\n",
|
||||
" modification = True\n",
|
||||
"\n",
|
||||
" while modification:\n",
|
||||
" # reset modification\n",
|
||||
" modification = False\n",
|
||||
" for i in range(n):\n",
|
||||
" for j in range(n):\n",
|
||||
" # checking if there is a path from i to j\n",
|
||||
" if matrix[i][j] > 0:\n",
|
||||
" for k in range(j,n):\n",
|
||||
" # checking if there is a path from i k to to k\n",
|
||||
" if matrix[i][k] > 0 and matrix[j][k] > 0:\n",
|
||||
" matrix[i][k] = 0\n",
|
||||
" modification = True\n",
|
||||
" print(\"Removing \", i, k)\n",
|
||||
" # restart while loop\n",
|
||||
" break\n",
|
||||
" if modification:\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
" return matrix\n",
|
||||
"\n",
|
||||
"no_closure = remove_transitive_closure(transitive_closure_matrix)\n",
|
||||
"\n",
|
||||
"for row in no_closure:\n",
|
||||
" print(row)\n",
|
||||
"\n",
|
||||
"no_closure = remove_transitive_closure(m)\n",
|
||||
"\n",
|
||||
"for row in no_closure:\n",
|
||||
" print(row)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
488
TD3b/TD3.ipynb
Normal file
@@ -0,0 +1,488 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 78,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# matrices from the document\n",
|
||||
"R1 = [[1,0,1],[0,1,0],[1,0,0]]\n",
|
||||
"R2 = [[1,1,0],[0,0,1],[1,0,1]]\n",
|
||||
"R3 = [[1, 1, 1], [0, 0, 1], [0, 0, 1]]\n",
|
||||
"R4 = [[1, 1, 0, 1], [1, 1, 0, 1], [0, 0, 1, 0], [1, 1, 0, 1]]\n",
|
||||
"R5 = [[0, 1, 0], [0, 0, 1], [1, 0, 0]]\n",
|
||||
"R6 = [[1, 1, 1], [0, 0, 1], [1, 0, 0]]\n",
|
||||
"\n",
|
||||
"tests = [R1, R2, R3, R4, R5, R6]\n",
|
||||
"\n",
|
||||
"CHAMPIONS = [[1,1,0,1,1],\n",
|
||||
" [0,1,0,0,0],\n",
|
||||
" [1,1,1,1,1],\n",
|
||||
" [1,1,0,1,1],\n",
|
||||
" [0,1,0,0,1]]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 79,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Check if a matrix is reflexive\n",
|
||||
"def reflexive(R):\n",
|
||||
" for i in range(len(R)):\n",
|
||||
" if R[i][i] != 1:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"# Check if a matrix is symmetric\n",
|
||||
"def symmetrical(R):\n",
|
||||
" for i in range(len(R)):\n",
|
||||
" for j in range(len(R)):\n",
|
||||
" if R[i][j] != R[j][i]:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"# Check if a matrix is anti-symmetric\n",
|
||||
"def anti_symmetrical(R):\n",
|
||||
" for i in range(len(R)):\n",
|
||||
" for j in range(len(R)):\n",
|
||||
" if R[i][j] > 0 and R[j][i] > 0 and i != j:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"# Check if a matrix is asymmetrical\n",
|
||||
"def asymmetrical(R):\n",
|
||||
" for i in range(len(R)):\n",
|
||||
" for j in range(len(R)):\n",
|
||||
" if R[i][j] > 0 and R[j][i] > 0:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"# Check if a matrix is irreflexive\n",
|
||||
"def irreflexive(R):\n",
|
||||
" for i in range(len(R)):\n",
|
||||
" if R[i][i] > 0:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"# Check if a matrix is transitive\n",
|
||||
"def transitive(matrix):\n",
|
||||
" is_transitive = True\n",
|
||||
" for i in range(len(matrix)):\n",
|
||||
" for j in range(len(matrix)):\n",
|
||||
" if matrix[i][j] > 0:\n",
|
||||
" for k in range(len(matrix)):\n",
|
||||
" if matrix[j][k] > 0:\n",
|
||||
" is_transitive = is_transitive and matrix[i][k] > 0\n",
|
||||
"\n",
|
||||
" return is_transitive\n",
|
||||
"\n",
|
||||
"# Check if a matrix is intransitive\n",
|
||||
"def intransitive(matrix):\n",
|
||||
" is_transitive = True\n",
|
||||
" for i in range(len(matrix)):\n",
|
||||
" for j in range(len(matrix)):\n",
|
||||
" if matrix[i][j] > 0:\n",
|
||||
" for k in range(len(matrix)):\n",
|
||||
" if matrix[j][k] > 0:\n",
|
||||
" is_transitive = is_transitive and matrix[i][k] != 1\n",
|
||||
"\n",
|
||||
" return is_transitive\n",
|
||||
"\n",
|
||||
"# Check if a matrix is non-transitive\n",
|
||||
"def non_transitive(matrix):\n",
|
||||
" n = len(matrix)\n",
|
||||
"\n",
|
||||
" for i in range(n):\n",
|
||||
" for j in range(n):\n",
|
||||
" if matrix[i][j] > 0:\n",
|
||||
" for k in range(n):\n",
|
||||
" if matrix[j][k] > 0 and matrix[i][k] == 0:\n",
|
||||
" return True\n",
|
||||
" return False"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 80,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 1\n",
|
||||
"reflexive: False\n",
|
||||
"irreflexive: False\n",
|
||||
"symmetrical: True\n",
|
||||
"anti_symmetrical: False\n",
|
||||
"asymmetrical: False\n",
|
||||
"transitive: False\n",
|
||||
"intransitive: False\n",
|
||||
"non_transitive: True\n",
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 2\n",
|
||||
"reflexive: False\n",
|
||||
"irreflexive: False\n",
|
||||
"symmetrical: False\n",
|
||||
"anti_symmetrical: True\n",
|
||||
"asymmetrical: False\n",
|
||||
"transitive: False\n",
|
||||
"intransitive: False\n",
|
||||
"non_transitive: True\n",
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 3\n",
|
||||
"reflexive: False\n",
|
||||
"irreflexive: False\n",
|
||||
"symmetrical: False\n",
|
||||
"anti_symmetrical: True\n",
|
||||
"asymmetrical: False\n",
|
||||
"transitive: True\n",
|
||||
"intransitive: False\n",
|
||||
"non_transitive: False\n",
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 4\n",
|
||||
"reflexive: True\n",
|
||||
"irreflexive: False\n",
|
||||
"symmetrical: True\n",
|
||||
"anti_symmetrical: False\n",
|
||||
"asymmetrical: False\n",
|
||||
"transitive: True\n",
|
||||
"intransitive: False\n",
|
||||
"non_transitive: False\n",
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 5\n",
|
||||
"reflexive: False\n",
|
||||
"irreflexive: True\n",
|
||||
"symmetrical: False\n",
|
||||
"anti_symmetrical: True\n",
|
||||
"asymmetrical: True\n",
|
||||
"transitive: False\n",
|
||||
"intransitive: True\n",
|
||||
"non_transitive: True\n",
|
||||
"--------------------------------------------------\n",
|
||||
"Matrix: R 6\n",
|
||||
"reflexive: False\n",
|
||||
"irreflexive: False\n",
|
||||
"symmetrical: False\n",
|
||||
"anti_symmetrical: False\n",
|
||||
"asymmetrical: False\n",
|
||||
"transitive: False\n",
|
||||
"intransitive: False\n",
|
||||
"non_transitive: True\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Testing all the matrices from the slides\n",
|
||||
"\n",
|
||||
"for i in range(len(tests)):\n",
|
||||
" print(\"--------------------------------------------------\")\n",
|
||||
" print(\"Matrix: R\", i+1)\n",
|
||||
" a = reflexive(tests[i])\n",
|
||||
" print(\"reflexive: \", a)\n",
|
||||
" a = irreflexive(tests[i])\n",
|
||||
" print(\"irreflexive: \", a)\n",
|
||||
" a = symmetrical(tests[i])\n",
|
||||
" print(\"symmetrical: \", a)\n",
|
||||
" a = anti_symmetrical(tests[i])\n",
|
||||
" print(\"anti_symmetrical: \", a)\n",
|
||||
" a = asymmetrical(tests[i])\n",
|
||||
" print(\"asymmetrical: \", a)\n",
|
||||
" a = transitive(tests[i])\n",
|
||||
" print(\"transitive: \", a)\n",
|
||||
" a = intransitive(tests[i])\n",
|
||||
" print(\"intransitive: \", a)\n",
|
||||
" a = non_transitive(tests[i])\n",
|
||||
" print(\"non_transitive: \", a)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 81,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Equivalence classes\n",
|
||||
"\n",
|
||||
"def equivalence_classes(R):\n",
|
||||
" n = len(R)\n",
|
||||
" classes = []\n",
|
||||
" scores = []\n",
|
||||
"\n",
|
||||
" # Count number of (x,y) existing on each row\n",
|
||||
" for x in range(n):\n",
|
||||
" score = 0\n",
|
||||
" for y in range(n):\n",
|
||||
" if R[x][y] > 0:\n",
|
||||
" score += 1\n",
|
||||
" scores.append(score)\n",
|
||||
"\n",
|
||||
" lastScore = 0\n",
|
||||
"\n",
|
||||
" for i in range(n):\n",
|
||||
" max_score = 0\n",
|
||||
" max_index = 0\n",
|
||||
" for j in range(n):\n",
|
||||
" if scores[j] > max_score:\n",
|
||||
" max_score = scores[j]\n",
|
||||
" max_index = j\n",
|
||||
"\n",
|
||||
" if(max_score == lastScore):\n",
|
||||
" classes[len(classes)-1].append(max_index)\n",
|
||||
" else:\n",
|
||||
" # Add the class to the list of classes\n",
|
||||
" classes.append([max_index])\n",
|
||||
"\n",
|
||||
" lastScore = max_score\n",
|
||||
" scores[max_index] = 0\n",
|
||||
"\n",
|
||||
" return classes, scores\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 82,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"([[2], [0, 3], [4], [1]], [0, 0, 0, 0, 0])\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(equivalence_classes(CHAMPIONS))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 83,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[1, 1, 1, 1]\n",
|
||||
"[0, 0, 0, 0]\n",
|
||||
"[1, 1, 1, 1]\n",
|
||||
"[1, 1, 1, 1]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def transitive_closure(adjacency_matrix):\n",
|
||||
" \"\"\"\n",
|
||||
" Calculate the transitive closure of a directed graph represented as an adjacency matrix.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" adjacency_matrix (list of lists): The adjacency matrix of the graph.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" list of lists: The transitive closure matrix.\n",
|
||||
" \"\"\"\n",
|
||||
" n = len(adjacency_matrix)\n",
|
||||
"\n",
|
||||
" # Initialize the transitive closure matrix as a copy of the adjacency matrix\n",
|
||||
" closure_matrix = [row[:] for row in adjacency_matrix]\n",
|
||||
"\n",
|
||||
" # Perform the Floyd-Warshall algorithm to compute transitive closure\n",
|
||||
" for k in range(n):\n",
|
||||
" for i in range(n):\n",
|
||||
" for j in range(n):\n",
|
||||
" closure_matrix[i][j] = closure_matrix[i][j] or (closure_matrix[i][k] and closure_matrix[k][j])\n",
|
||||
"\n",
|
||||
" return closure_matrix\n",
|
||||
"\n",
|
||||
"# Example usage:\n",
|
||||
"adjacency_matrix = [\n",
|
||||
" [0, 1, 0, 0],\n",
|
||||
" [0, 0, 0, 1],\n",
|
||||
" [1, 0, 0, 0],\n",
|
||||
" [0, 0, 1, 0]\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"ex_cours = [\n",
|
||||
" [0, 0, 1, 0],\n",
|
||||
" [0, 0, 0, 0],\n",
|
||||
" [0, 1, 1, 1],\n",
|
||||
" [1, 0, 0, 0]\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"transitive_closure_matrix = transitive_closure(ex_cours)\n",
|
||||
"\n",
|
||||
"# Print the transitive closure matrix\n",
|
||||
"for row in transitive_closure_matrix:\n",
|
||||
" print(row)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 84,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[3, 2, 1, 2]\n",
|
||||
"[0, 0, 0, 0]\n",
|
||||
"[2, 1, 1, 1]\n",
|
||||
"[1, 3, 2, 3]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Make transitive closure\n",
|
||||
"\n",
|
||||
"transitive_closure_matrix = [\n",
|
||||
" [1, 0, 0, 0],\n",
|
||||
" [0, 1, 0, 1],\n",
|
||||
" [1, 0, 0, 0],\n",
|
||||
" [0, 0, 1, 1]\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"def transitive_closure(matrix):\n",
|
||||
" modification = True\n",
|
||||
" while modification:\n",
|
||||
" modification = False\n",
|
||||
" for i in range(len(matrix)):\n",
|
||||
" for j in range(len(matrix)):\n",
|
||||
" for k in range(len(matrix)):\n",
|
||||
" if matrix[i][j] > 0 and matrix[j][k] > 0 and matrix[i][k] == 0:\n",
|
||||
" matrix[i][k] = matrix[i][j] + matrix[j][k]\n",
|
||||
" modification = True\n",
|
||||
" else:\n",
|
||||
" modification = False\n",
|
||||
"\n",
|
||||
" return matrix\n",
|
||||
"\n",
|
||||
"full_closure = transitive_closure(ex_cours)\n",
|
||||
"\n",
|
||||
"for row in full_closure:\n",
|
||||
" print(row)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 85,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Removing 0 0\n",
|
||||
"Removing 1 1\n",
|
||||
"Removing 1 3\n",
|
||||
"Removing 3 3\n",
|
||||
"[0, 0, 0, 0]\n",
|
||||
"[0, 0, 2, 0]\n",
|
||||
"[1, 0, 0, 0]\n",
|
||||
"[2, 0, 1, 0]\n",
|
||||
"Removing 0 0\n",
|
||||
"Removing 1 1\n",
|
||||
"Removing 2 2\n",
|
||||
"Removing 3 3\n",
|
||||
"Removing 4 4\n",
|
||||
"Removing 5 5\n",
|
||||
"Removing 6 6\n",
|
||||
"Removing 0 4\n",
|
||||
"Removing 1 5\n",
|
||||
"Removing 3 5\n",
|
||||
"Removing 4 6\n",
|
||||
"Removing 0 6\n",
|
||||
"[0, 1, 1, 1, 0, 1, 0]\n",
|
||||
"[0, 0, 0, 0, 1, 0, 1]\n",
|
||||
"[0, 0, 0, 0, 0, 0, 1]\n",
|
||||
"[0, 0, 0, 0, 1, 0, 1]\n",
|
||||
"[0, 0, 0, 0, 0, 1, 0]\n",
|
||||
"[0, 0, 0, 0, 0, 0, 1]\n",
|
||||
"[0, 0, 0, 0, 0, 0, 0]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Remove transitive closure\n",
|
||||
"\n",
|
||||
"transitive_closure_matrix = [\n",
|
||||
"[1, 0, 0, 0],\n",
|
||||
"[0, 1, 2, 1],\n",
|
||||
"[1, 0, 0, 0],\n",
|
||||
"[2, 0, 1, 1]\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"m = [\n",
|
||||
" [1,1,1,1,1,1,1],\n",
|
||||
" [0,1,0,0,1,1,1],\n",
|
||||
" [0,0,1,0,0,0,1],\n",
|
||||
" [0,0,0,1,1,1,1],\n",
|
||||
" [0,0,0,0,1,1,1],\n",
|
||||
" [0,0,0,0,0,1,1],\n",
|
||||
" [0,0,0,0,0,0,1]\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"def remove_transitive_closure(matrix):\n",
|
||||
" n = len(matrix)\n",
|
||||
" modification = True\n",
|
||||
"\n",
|
||||
" while modification:\n",
|
||||
" # reset modification\n",
|
||||
" modification = False\n",
|
||||
" for i in range(n):\n",
|
||||
" for j in range(n):\n",
|
||||
" # checking if there is a path from i to j\n",
|
||||
" if matrix[i][j] > 0:\n",
|
||||
" for k in range(j,n):\n",
|
||||
" # checking if there is a path from i k to to k\n",
|
||||
" if matrix[i][k] > 0 and matrix[j][k] > 0:\n",
|
||||
" matrix[i][k] = 0\n",
|
||||
" modification = True\n",
|
||||
" print(\"Removing \", i, k)\n",
|
||||
" # restart while loop\n",
|
||||
" break\n",
|
||||
" if modification:\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
" return matrix\n",
|
||||
"\n",
|
||||
"no_closure = remove_transitive_closure(transitive_closure_matrix)\n",
|
||||
"\n",
|
||||
"for row in no_closure:\n",
|
||||
" print(row)\n",
|
||||
"\n",
|
||||
"no_closure = remove_transitive_closure(m)\n",
|
||||
"\n",
|
||||
"for row in no_closure:\n",
|
||||
" print(row)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
406
TD3b/TD3.md
Normal file
@@ -0,0 +1,406 @@
|
||||
```python
|
||||
# matrices from the document
|
||||
R1 = [[1,0,1],[0,1,0],[1,0,0]]
|
||||
R2 = [[1,1,0],[0,0,1],[1,0,1]]
|
||||
R3 = [[1, 1, 1], [0, 0, 1], [0, 0, 1]]
|
||||
R4 = [[1, 1, 0, 1], [1, 1, 0, 1], [0, 0, 1, 0], [1, 1, 0, 1]]
|
||||
R5 = [[0, 1, 0], [0, 0, 1], [1, 0, 0]]
|
||||
R6 = [[1, 1, 1], [0, 0, 1], [1, 0, 0]]
|
||||
|
||||
tests = [R1, R2, R3, R4, R5, R6]
|
||||
|
||||
CHAMPIONS = [[1,1,0,1,1],
|
||||
[0,1,0,0,0],
|
||||
[1,1,1,1,1],
|
||||
[1,1,0,1,1],
|
||||
[0,1,0,0,1]]
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
# Check if a matrix is reflexive
|
||||
def reflexive(R):
|
||||
for i in range(len(R)):
|
||||
if R[i][i] != 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Check if a matrix is symmetric
|
||||
def symmetrical(R):
|
||||
for i in range(len(R)):
|
||||
for j in range(len(R)):
|
||||
if R[i][j] != R[j][i]:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Check if a matrix is anti-symmetric
|
||||
def anti_symmetrical(R):
|
||||
for i in range(len(R)):
|
||||
for j in range(len(R)):
|
||||
if R[i][j] > 0 and R[j][i] > 0 and i != j:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Check if a matrix is asymmetrical
|
||||
def asymmetrical(R):
|
||||
for i in range(len(R)):
|
||||
for j in range(len(R)):
|
||||
if R[i][j] > 0 and R[j][i] > 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Check if a matrix is irreflexive
|
||||
def irreflexive(R):
|
||||
for i in range(len(R)):
|
||||
if R[i][i] > 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Check if a matrix is transitive
|
||||
def transitive(matrix):
|
||||
is_transitive = True
|
||||
for i in range(len(matrix)):
|
||||
for j in range(len(matrix)):
|
||||
if matrix[i][j] > 0:
|
||||
for k in range(len(matrix)):
|
||||
if matrix[j][k] > 0:
|
||||
is_transitive = is_transitive and matrix[i][k] > 0
|
||||
|
||||
return is_transitive
|
||||
|
||||
# Check if a matrix is intransitive
|
||||
def intransitive(matrix):
|
||||
is_transitive = True
|
||||
for i in range(len(matrix)):
|
||||
for j in range(len(matrix)):
|
||||
if matrix[i][j] > 0:
|
||||
for k in range(len(matrix)):
|
||||
if matrix[j][k] > 0:
|
||||
is_transitive = is_transitive and matrix[i][k] != 1
|
||||
|
||||
return is_transitive
|
||||
|
||||
# Check if a matrix is non-transitive
|
||||
def non_transitive(matrix):
|
||||
n = len(matrix)
|
||||
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
if matrix[i][j] > 0:
|
||||
for k in range(n):
|
||||
if matrix[j][k] > 0 and matrix[i][k] == 0:
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
# Testing all the matrices from the slides
|
||||
|
||||
for i in range(len(tests)):
|
||||
print("--------------------------------------------------")
|
||||
print("Matrix: R", i+1)
|
||||
a = reflexive(tests[i])
|
||||
print("reflexive: ", a)
|
||||
a = irreflexive(tests[i])
|
||||
print("irreflexive: ", a)
|
||||
a = symmetrical(tests[i])
|
||||
print("symmetrical: ", a)
|
||||
a = anti_symmetrical(tests[i])
|
||||
print("anti_symmetrical: ", a)
|
||||
a = asymmetrical(tests[i])
|
||||
print("asymmetrical: ", a)
|
||||
a = transitive(tests[i])
|
||||
print("transitive: ", a)
|
||||
a = intransitive(tests[i])
|
||||
print("intransitive: ", a)
|
||||
a = non_transitive(tests[i])
|
||||
print("non_transitive: ", a)
|
||||
```
|
||||
|
||||
--------------------------------------------------
|
||||
Matrix: R 1
|
||||
reflexive: False
|
||||
irreflexive: False
|
||||
symmetrical: True
|
||||
anti_symmetrical: False
|
||||
asymmetrical: False
|
||||
transitive: False
|
||||
intransitive: False
|
||||
non_transitive: True
|
||||
--------------------------------------------------
|
||||
Matrix: R 2
|
||||
reflexive: False
|
||||
irreflexive: False
|
||||
symmetrical: False
|
||||
anti_symmetrical: True
|
||||
asymmetrical: False
|
||||
transitive: False
|
||||
intransitive: False
|
||||
non_transitive: True
|
||||
--------------------------------------------------
|
||||
Matrix: R 3
|
||||
reflexive: False
|
||||
irreflexive: False
|
||||
symmetrical: False
|
||||
anti_symmetrical: True
|
||||
asymmetrical: False
|
||||
transitive: True
|
||||
intransitive: False
|
||||
non_transitive: False
|
||||
--------------------------------------------------
|
||||
Matrix: R 4
|
||||
reflexive: True
|
||||
irreflexive: False
|
||||
symmetrical: True
|
||||
anti_symmetrical: False
|
||||
asymmetrical: False
|
||||
transitive: True
|
||||
intransitive: False
|
||||
non_transitive: False
|
||||
--------------------------------------------------
|
||||
Matrix: R 5
|
||||
reflexive: False
|
||||
irreflexive: True
|
||||
symmetrical: False
|
||||
anti_symmetrical: True
|
||||
asymmetrical: True
|
||||
transitive: False
|
||||
intransitive: True
|
||||
non_transitive: True
|
||||
--------------------------------------------------
|
||||
Matrix: R 6
|
||||
reflexive: False
|
||||
irreflexive: False
|
||||
symmetrical: False
|
||||
anti_symmetrical: False
|
||||
asymmetrical: False
|
||||
transitive: False
|
||||
intransitive: False
|
||||
non_transitive: True
|
||||
|
||||
|
||||
|
||||
```python
|
||||
# Equivalence classes
|
||||
|
||||
def equivalence_classes(R):
|
||||
n = len(R)
|
||||
classes = []
|
||||
scores = []
|
||||
|
||||
# Count number of (x,y) existing on each row
|
||||
for x in range(n):
|
||||
score = 0
|
||||
for y in range(n):
|
||||
if R[x][y] > 0:
|
||||
score += 1
|
||||
scores.append(score)
|
||||
|
||||
lastScore = 0
|
||||
|
||||
for i in range(n):
|
||||
max_score = 0
|
||||
max_index = 0
|
||||
for j in range(n):
|
||||
if scores[j] > max_score:
|
||||
max_score = scores[j]
|
||||
max_index = j
|
||||
|
||||
if(max_score == lastScore):
|
||||
classes[len(classes)-1].append(max_index)
|
||||
else:
|
||||
# Add the class to the list of classes
|
||||
classes.append([max_index])
|
||||
|
||||
lastScore = max_score
|
||||
scores[max_index] = 0
|
||||
|
||||
return classes, scores
|
||||
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
print(equivalence_classes(CHAMPIONS))
|
||||
```
|
||||
|
||||
([[2], [0, 3], [4], [1]], [0, 0, 0, 0, 0])
|
||||
|
||||
|
||||
|
||||
```python
|
||||
def transitive_closure(adjacency_matrix):
|
||||
"""
|
||||
Calculate the transitive closure of a directed graph represented as an adjacency matrix.
|
||||
|
||||
Args:
|
||||
adjacency_matrix (list of lists): The adjacency matrix of the graph.
|
||||
|
||||
Returns:
|
||||
list of lists: The transitive closure matrix.
|
||||
"""
|
||||
n = len(adjacency_matrix)
|
||||
|
||||
# Initialize the transitive closure matrix as a copy of the adjacency matrix
|
||||
closure_matrix = [row[:] for row in adjacency_matrix]
|
||||
|
||||
# Perform the Floyd-Warshall algorithm to compute transitive closure
|
||||
for k in range(n):
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
closure_matrix[i][j] = closure_matrix[i][j] or (closure_matrix[i][k] and closure_matrix[k][j])
|
||||
|
||||
return closure_matrix
|
||||
|
||||
# Example usage:
|
||||
adjacency_matrix = [
|
||||
[0, 1, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[1, 0, 0, 0],
|
||||
[0, 0, 1, 0]
|
||||
]
|
||||
|
||||
ex_cours = [
|
||||
[0, 0, 1, 0],
|
||||
[0, 0, 0, 0],
|
||||
[0, 1, 1, 1],
|
||||
[1, 0, 0, 0]
|
||||
]
|
||||
|
||||
transitive_closure_matrix = transitive_closure(ex_cours)
|
||||
|
||||
# Print the transitive closure matrix
|
||||
for row in transitive_closure_matrix:
|
||||
print(row)
|
||||
|
||||
```
|
||||
|
||||
[1, 1, 1, 1]
|
||||
[0, 0, 0, 0]
|
||||
[1, 1, 1, 1]
|
||||
[1, 1, 1, 1]
|
||||
|
||||
|
||||
|
||||
```python
|
||||
# Make transitive closure
|
||||
|
||||
transitive_closure_matrix = [
|
||||
[1, 0, 0, 0],
|
||||
[0, 1, 0, 1],
|
||||
[1, 0, 0, 0],
|
||||
[0, 0, 1, 1]
|
||||
]
|
||||
|
||||
def transitive_closure(matrix):
|
||||
modification = True
|
||||
while modification:
|
||||
modification = False
|
||||
for i in range(len(matrix)):
|
||||
for j in range(len(matrix)):
|
||||
for k in range(len(matrix)):
|
||||
if matrix[i][j] > 0 and matrix[j][k] > 0 and matrix[i][k] == 0:
|
||||
matrix[i][k] = matrix[i][j] + matrix[j][k]
|
||||
modification = True
|
||||
else:
|
||||
modification = False
|
||||
|
||||
return matrix
|
||||
|
||||
full_closure = transitive_closure(ex_cours)
|
||||
|
||||
for row in full_closure:
|
||||
print(row)
|
||||
```
|
||||
|
||||
[3, 2, 1, 2]
|
||||
[0, 0, 0, 0]
|
||||
[2, 1, 1, 1]
|
||||
[1, 3, 2, 3]
|
||||
|
||||
|
||||
|
||||
```python
|
||||
# Remove transitive closure
|
||||
|
||||
transitive_closure_matrix = [
|
||||
[1, 0, 0, 0],
|
||||
[0, 1, 2, 1],
|
||||
[1, 0, 0, 0],
|
||||
[2, 0, 1, 1]
|
||||
]
|
||||
|
||||
m = [
|
||||
[1,1,1,1,1,1,1],
|
||||
[0,1,0,0,1,1,1],
|
||||
[0,0,1,0,0,0,1],
|
||||
[0,0,0,1,1,1,1],
|
||||
[0,0,0,0,1,1,1],
|
||||
[0,0,0,0,0,1,1],
|
||||
[0,0,0,0,0,0,1]
|
||||
]
|
||||
|
||||
def remove_transitive_closure(matrix):
|
||||
n = len(matrix)
|
||||
modification = True
|
||||
|
||||
while modification:
|
||||
# reset modification
|
||||
modification = False
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
# checking if there is a path from i to j
|
||||
if matrix[i][j] > 0:
|
||||
for k in range(j,n):
|
||||
# checking if there is a path from i k to to k
|
||||
if matrix[i][k] > 0 and matrix[j][k] > 0:
|
||||
matrix[i][k] = 0
|
||||
modification = True
|
||||
print("Removing ", i, k)
|
||||
# restart while loop
|
||||
break
|
||||
if modification:
|
||||
break
|
||||
|
||||
return matrix
|
||||
|
||||
no_closure = remove_transitive_closure(transitive_closure_matrix)
|
||||
|
||||
for row in no_closure:
|
||||
print(row)
|
||||
|
||||
no_closure = remove_transitive_closure(m)
|
||||
|
||||
for row in no_closure:
|
||||
print(row)
|
||||
```
|
||||
|
||||
Removing 0 0
|
||||
Removing 1 1
|
||||
Removing 1 3
|
||||
Removing 3 3
|
||||
[0, 0, 0, 0]
|
||||
[0, 0, 2, 0]
|
||||
[1, 0, 0, 0]
|
||||
[2, 0, 1, 0]
|
||||
Removing 0 0
|
||||
Removing 1 1
|
||||
Removing 2 2
|
||||
Removing 3 3
|
||||
Removing 4 4
|
||||
Removing 5 5
|
||||
Removing 6 6
|
||||
Removing 0 4
|
||||
Removing 1 5
|
||||
Removing 3 5
|
||||
Removing 4 6
|
||||
Removing 0 6
|
||||
[0, 1, 1, 1, 0, 1, 0]
|
||||
[0, 0, 0, 0, 1, 0, 1]
|
||||
[0, 0, 0, 0, 0, 0, 1]
|
||||
[0, 0, 0, 0, 1, 0, 1]
|
||||
[0, 0, 0, 0, 0, 1, 0]
|
||||
[0, 0, 0, 0, 0, 0, 1]
|
||||
[0, 0, 0, 0, 0, 0, 0]
|
||||
|
||||
BIN
TD3b/TD3.pdf
Normal file
310
TD3b/TD3.py
Normal file
@@ -0,0 +1,310 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# In[78]:
|
||||
|
||||
|
||||
# matrices from the document
|
||||
R1 = [[1,0,1],[0,1,0],[1,0,0]]
|
||||
R2 = [[1,1,0],[0,0,1],[1,0,1]]
|
||||
R3 = [[1, 1, 1], [0, 0, 1], [0, 0, 1]]
|
||||
R4 = [[1, 1, 0, 1], [1, 1, 0, 1], [0, 0, 1, 0], [1, 1, 0, 1]]
|
||||
R5 = [[0, 1, 0], [0, 0, 1], [1, 0, 0]]
|
||||
R6 = [[1, 1, 1], [0, 0, 1], [1, 0, 0]]
|
||||
|
||||
tests = [R1, R2, R3, R4, R5, R6]
|
||||
|
||||
CHAMPIONS = [[1,1,0,1,1],
|
||||
[0,1,0,0,0],
|
||||
[1,1,1,1,1],
|
||||
[1,1,0,1,1],
|
||||
[0,1,0,0,1]]
|
||||
|
||||
|
||||
# In[79]:
|
||||
|
||||
|
||||
# Check if a matrix is reflexive
|
||||
def reflexive(R):
|
||||
for i in range(len(R)):
|
||||
if R[i][i] != 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Check if a matrix is symmetric
|
||||
def symmetrical(R):
|
||||
for i in range(len(R)):
|
||||
for j in range(len(R)):
|
||||
if R[i][j] != R[j][i]:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Check if a matrix is anti-symmetric
|
||||
def anti_symmetrical(R):
|
||||
for i in range(len(R)):
|
||||
for j in range(len(R)):
|
||||
if R[i][j] > 0 and R[j][i] > 0 and i != j:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Check if a matrix is asymmetrical
|
||||
def asymmetrical(R):
|
||||
for i in range(len(R)):
|
||||
for j in range(len(R)):
|
||||
if R[i][j] > 0 and R[j][i] > 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Check if a matrix is irreflexive
|
||||
def irreflexive(R):
|
||||
for i in range(len(R)):
|
||||
if R[i][i] > 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
# Check if a matrix is transitive
|
||||
def transitive(matrix):
|
||||
is_transitive = True
|
||||
for i in range(len(matrix)):
|
||||
for j in range(len(matrix)):
|
||||
if matrix[i][j] > 0:
|
||||
for k in range(len(matrix)):
|
||||
if matrix[j][k] > 0:
|
||||
is_transitive = is_transitive and matrix[i][k] > 0
|
||||
|
||||
return is_transitive
|
||||
|
||||
# Check if a matrix is intransitive
|
||||
def intransitive(matrix):
|
||||
is_transitive = True
|
||||
for i in range(len(matrix)):
|
||||
for j in range(len(matrix)):
|
||||
if matrix[i][j] > 0:
|
||||
for k in range(len(matrix)):
|
||||
if matrix[j][k] > 0:
|
||||
is_transitive = is_transitive and matrix[i][k] != 1
|
||||
|
||||
return is_transitive
|
||||
|
||||
# Check if a matrix is non-transitive
|
||||
def non_transitive(matrix):
|
||||
n = len(matrix)
|
||||
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
if matrix[i][j] > 0:
|
||||
for k in range(n):
|
||||
if matrix[j][k] > 0 and matrix[i][k] == 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# In[80]:
|
||||
|
||||
|
||||
# Testing all the matrices from the slides
|
||||
|
||||
for i in range(len(tests)):
|
||||
print("--------------------------------------------------")
|
||||
print("Matrix: R", i+1)
|
||||
a = reflexive(tests[i])
|
||||
print("reflexive: ", a)
|
||||
a = irreflexive(tests[i])
|
||||
print("irreflexive: ", a)
|
||||
a = symmetrical(tests[i])
|
||||
print("symmetrical: ", a)
|
||||
a = anti_symmetrical(tests[i])
|
||||
print("anti_symmetrical: ", a)
|
||||
a = asymmetrical(tests[i])
|
||||
print("asymmetrical: ", a)
|
||||
a = transitive(tests[i])
|
||||
print("transitive: ", a)
|
||||
a = intransitive(tests[i])
|
||||
print("intransitive: ", a)
|
||||
a = non_transitive(tests[i])
|
||||
print("non_transitive: ", a)
|
||||
|
||||
|
||||
# In[81]:
|
||||
|
||||
|
||||
# Equivalence classes
|
||||
|
||||
def equivalence_classes(R):
|
||||
n = len(R)
|
||||
classes = []
|
||||
scores = []
|
||||
|
||||
# Count number of (x,y) existing on each row
|
||||
for x in range(n):
|
||||
score = 0
|
||||
for y in range(n):
|
||||
if R[x][y] > 0:
|
||||
score += 1
|
||||
scores.append(score)
|
||||
|
||||
lastScore = 0
|
||||
|
||||
for i in range(n):
|
||||
max_score = 0
|
||||
max_index = 0
|
||||
for j in range(n):
|
||||
if scores[j] > max_score:
|
||||
max_score = scores[j]
|
||||
max_index = j
|
||||
|
||||
if(max_score == lastScore):
|
||||
classes[len(classes)-1].append(max_index)
|
||||
else:
|
||||
# Add the class to the list of classes
|
||||
classes.append([max_index])
|
||||
|
||||
lastScore = max_score
|
||||
scores[max_index] = 0
|
||||
|
||||
return classes, scores
|
||||
|
||||
|
||||
# In[82]:
|
||||
|
||||
|
||||
print(equivalence_classes(CHAMPIONS))
|
||||
|
||||
|
||||
# In[83]:
|
||||
|
||||
|
||||
def transitive_closure(adjacency_matrix):
|
||||
"""
|
||||
Calculate the transitive closure of a directed graph represented as an adjacency matrix.
|
||||
|
||||
Args:
|
||||
adjacency_matrix (list of lists): The adjacency matrix of the graph.
|
||||
|
||||
Returns:
|
||||
list of lists: The transitive closure matrix.
|
||||
"""
|
||||
n = len(adjacency_matrix)
|
||||
|
||||
# Initialize the transitive closure matrix as a copy of the adjacency matrix
|
||||
closure_matrix = [row[:] for row in adjacency_matrix]
|
||||
|
||||
# Perform the Floyd-Warshall algorithm to compute transitive closure
|
||||
for k in range(n):
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
closure_matrix[i][j] = closure_matrix[i][j] or (closure_matrix[i][k] and closure_matrix[k][j])
|
||||
|
||||
return closure_matrix
|
||||
|
||||
# Example usage:
|
||||
adjacency_matrix = [
|
||||
[0, 1, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[1, 0, 0, 0],
|
||||
[0, 0, 1, 0]
|
||||
]
|
||||
|
||||
ex_cours = [
|
||||
[0, 0, 1, 0],
|
||||
[0, 0, 0, 0],
|
||||
[0, 1, 1, 1],
|
||||
[1, 0, 0, 0]
|
||||
]
|
||||
|
||||
transitive_closure_matrix = transitive_closure(ex_cours)
|
||||
|
||||
# Print the transitive closure matrix
|
||||
for row in transitive_closure_matrix:
|
||||
print(row)
|
||||
|
||||
|
||||
# In[84]:
|
||||
|
||||
|
||||
# Make transitive closure
|
||||
|
||||
transitive_closure_matrix = [
|
||||
[1, 0, 0, 0],
|
||||
[0, 1, 0, 1],
|
||||
[1, 0, 0, 0],
|
||||
[0, 0, 1, 1]
|
||||
]
|
||||
|
||||
def transitive_closure(matrix):
|
||||
modification = True
|
||||
while modification:
|
||||
modification = False
|
||||
for i in range(len(matrix)):
|
||||
for j in range(len(matrix)):
|
||||
for k in range(len(matrix)):
|
||||
if matrix[i][j] > 0 and matrix[j][k] > 0 and matrix[i][k] == 0:
|
||||
matrix[i][k] = matrix[i][j] + matrix[j][k]
|
||||
modification = True
|
||||
else:
|
||||
modification = False
|
||||
|
||||
return matrix
|
||||
|
||||
full_closure = transitive_closure(ex_cours)
|
||||
|
||||
for row in full_closure:
|
||||
print(row)
|
||||
|
||||
|
||||
# In[85]:
|
||||
|
||||
|
||||
# Remove transitive closure
|
||||
|
||||
transitive_closure_matrix = [
|
||||
[1, 0, 0, 0],
|
||||
[0, 1, 2, 1],
|
||||
[1, 0, 0, 0],
|
||||
[2, 0, 1, 1]
|
||||
]
|
||||
|
||||
m = [
|
||||
[1,1,1,1,1,1,1],
|
||||
[0,1,0,0,1,1,1],
|
||||
[0,0,1,0,0,0,1],
|
||||
[0,0,0,1,1,1,1],
|
||||
[0,0,0,0,1,1,1],
|
||||
[0,0,0,0,0,1,1],
|
||||
[0,0,0,0,0,0,1]
|
||||
]
|
||||
|
||||
def remove_transitive_closure(matrix):
|
||||
n = len(matrix)
|
||||
modification = True
|
||||
|
||||
while modification:
|
||||
# reset modification
|
||||
modification = False
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
# checking if there is a path from i to j
|
||||
if matrix[i][j] > 0:
|
||||
for k in range(j,n):
|
||||
# checking if there is a path from i k to to k
|
||||
if matrix[i][k] > 0 and matrix[j][k] > 0:
|
||||
matrix[i][k] = 0
|
||||
modification = True
|
||||
print("Removing ", i, k)
|
||||
# restart while loop
|
||||
break
|
||||
if modification:
|
||||
break
|
||||
|
||||
return matrix
|
||||
|
||||
no_closure = remove_transitive_closure(transitive_closure_matrix)
|
||||
|
||||
for row in no_closure:
|
||||
print(row)
|
||||
|
||||
no_closure = remove_transitive_closure(m)
|
||||
|
||||
for row in no_closure:
|
||||
print(row)
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# In[33]:
|
||||
|
||||
|
||||
# Data
|
||||
amounts = [12.35, 12.35*2, 12.35*3, 12.35*4, 12.35*5, 12.35*6, 12.35*7, 12.35*8, 12.35*9, 12.35*10]
|
||||
|
||||
coin_list = [
|
||||
[0.2, 0.1, 0.05],
|
||||
[1, 0.2, 0.1, 0.05],
|
||||
[2, 1, 0.2, 0.1, 0.05],
|
||||
[5, 2, 1, 0.2, 0.1, 0.05],
|
||||
[5, 2, 1, 0.5, 0.2, 0.1, 0.05],
|
||||
[5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.02],
|
||||
[5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01]
|
||||
]
|
||||
|
||||
import time
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
# In[34]:
|
||||
|
||||
|
||||
# Greedy
|
||||
def greedy_make_change(amount, coins):
|
||||
i = 0
|
||||
change = []
|
||||
|
||||
while amount > 0 and len(coins) > i:
|
||||
# print(str(round(amount//coins[i])) + " Coins of " + str(coins[i]) + "€")
|
||||
for j in range(round(amount//coins[i])):
|
||||
change.append(coins[i])
|
||||
amount = round(amount%coins[i], 2)
|
||||
i = i+1
|
||||
if amount > 0:
|
||||
print(f"Cannot make exact change for {amount:.2f}€")
|
||||
return change
|
||||
|
||||
|
||||
# In[35]:
|
||||
|
||||
|
||||
# Iterative
|
||||
|
||||
# Function to count occurrences of items in a list
|
||||
def count_occurrences(array):
|
||||
counts = {}
|
||||
for item in array:
|
||||
if item in counts:
|
||||
counts[item] += 1 # If the item is already in the dictionary, increment its count
|
||||
else:
|
||||
counts[item] = 1 # If the item is not in the dictionary, add it with a count of 1
|
||||
return counts
|
||||
|
||||
# Function to calculate all combinations of coins to make a specific amount
|
||||
def calculate_change_combinations(amount, coins):
|
||||
# Convert euro amounts to cents for calculations
|
||||
amount_cents = int(amount * 100)
|
||||
coin_values_cents = [int(coin * 100) for coin in coins]
|
||||
|
||||
# Initialize a list to store combinations and their counts
|
||||
combinations = []
|
||||
stack = [(0, [], 0)] # (current amount in cents, current combination, current coin index)
|
||||
|
||||
while stack:
|
||||
current_amount, current_combination, current_coin_index = stack.pop()
|
||||
|
||||
# If the current combination sums up to the target amount, add it to the list
|
||||
if current_amount == amount_cents:
|
||||
combinations.append(current_combination)
|
||||
# If the current amount is less than the target amount and there are more coins to consider
|
||||
elif current_amount < amount_cents and current_coin_index < len(coin_values_cents):
|
||||
coin = coin_values_cents[current_coin_index]
|
||||
max_count = (amount_cents - current_amount) // coin # Maximum count of the current coin
|
||||
|
||||
# Try adding different counts of the current coin to explore possibilities
|
||||
for count in range(max_count + 1):
|
||||
new_amount = current_amount + count * coin
|
||||
new_combination = current_combination + [coins[current_coin_index]] * count
|
||||
# Push the new state onto the stack for further exploration
|
||||
stack.append((new_amount, new_combination, current_coin_index + 1))
|
||||
|
||||
# Print the total number of combinations
|
||||
# print(f"Total number of combinations: {len(combinations)}")
|
||||
return combinations
|
||||
|
||||
|
||||
# In[36]:
|
||||
|
||||
|
||||
# Recursive
|
||||
def make_change_recursive(amount, coins, start, current_change, result):
|
||||
if amount == 0:
|
||||
result.append(current_change[:])
|
||||
return
|
||||
|
||||
for i in range(start, len(coins)):
|
||||
coin_cents = round(coins[i] * 100)
|
||||
if amount >= coin_cents:
|
||||
current_change.append(coins[i])
|
||||
make_change_recursive(amount - coin_cents, coins, i, current_change, result)
|
||||
current_change.pop()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# In[50]:
|
||||
|
||||
|
||||
greedy_times_coin_list = []
|
||||
greedy_labels_coin_list = []
|
||||
|
||||
coins_list_copy = coin_list.copy()
|
||||
|
||||
for coins in coins_list_copy:
|
||||
start = time.perf_counter()
|
||||
greedy_make_change(amounts[0], coins)
|
||||
end = time.perf_counter()
|
||||
greedy_times_coin_list.append(end - start)
|
||||
greedy_labels_coin_list.append(str(len(coins)))
|
||||
|
||||
greedy_times_amount_list = []
|
||||
greedy_labels_amount_list = []
|
||||
|
||||
# Plot a graph
|
||||
plt.figure(figsize=(10, 6))
|
||||
|
||||
# NO BAR
|
||||
plt.plot(greedy_labels_coin_list[1:], greedy_times_coin_list[1:], marker='o', linestyle='solid')
|
||||
plt.xlabel('Coins')
|
||||
plt.ylabel('Time (s)')
|
||||
plt.title('Greedy Algorithm using different coins to make change for 12.35€')
|
||||
|
||||
plt.show()
|
||||
|
||||
for amount in amounts:
|
||||
start = time.perf_counter()
|
||||
greedy_make_change(amount, coin_list[-1])
|
||||
end = time.perf_counter()
|
||||
greedy_times_amount_list.append(end - start)
|
||||
greedy_labels_amount_list.append(str(amount))
|
||||
|
||||
# NO BAR
|
||||
plt.plot(greedy_labels_amount_list[1:], greedy_times_amount_list[1:], marker='o', linestyle='solid')
|
||||
plt.xlabel('Amount')
|
||||
plt.ylabel('Time (s)')
|
||||
plt.title('Greedy Algorithm using 9 coins to make change for different amounts')
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
# In[47]:
|
||||
|
||||
|
||||
iterative_times_coin_list = []
|
||||
iterative_labels_coin_list = []
|
||||
|
||||
coins_list_copy = coin_list.copy()
|
||||
|
||||
for coins in coins_list_copy[:5]:
|
||||
start = time.perf_counter()
|
||||
calculate_change_combinations(amounts[0], coins)
|
||||
end = time.perf_counter()
|
||||
iterative_times_coin_list.append(end - start)
|
||||
iterative_labels_coin_list.append(str(len(coins)))
|
||||
|
||||
# Plot a graph
|
||||
plt.figure(figsize=(10, 6))
|
||||
|
||||
# NO BAR
|
||||
plt.plot(iterative_labels_coin_list[1:], iterative_times_coin_list[1:], marker='o', linestyle='solid')
|
||||
plt.xlabel('Coins')
|
||||
plt.ylabel('Time (s)')
|
||||
plt.title('Iterative Algorithm generating all the possibilities using different coins to make change for 12.35€')
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
# The complexity increase more when we add coins that are smaller. We can see that adding 0.02 and 0.01 have more impact than adding 2, and 5.
|
||||
|
||||
# In[46]:
|
||||
|
||||
|
||||
recursive_times_coin_list = []
|
||||
recursive_labels_coin_list = []
|
||||
|
||||
coins_list_copy = coin_list.copy()
|
||||
|
||||
for coins in coins_list_copy[:5]:
|
||||
# print(coins)
|
||||
start = time.perf_counter()
|
||||
make_change_recursive(amounts[0]*100, coins, 0, [], [])
|
||||
end = time.perf_counter()
|
||||
recursive_times_coin_list.append(end - start)
|
||||
recursive_labels_coin_list.append(str(len(coins)))
|
||||
|
||||
# Plot a graph
|
||||
plt.figure(figsize=(10, 6))
|
||||
|
||||
# NO BAR
|
||||
plt.plot(recursive_labels_coin_list[1:], recursive_times_coin_list[1:], marker='o', linestyle='solid')
|
||||
plt.xlabel('Coins')
|
||||
plt.ylabel('Time (s)')
|
||||
plt.title('Recursive Algorithm generating all the possibilities using different coins to make change for 12.35€')
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
# Complexity seems polynomial or exponential, but it is not possible to test using this problem as it takes to much time to run on all the list of coins.
|
||||
|
||||
# In[45]:
|
||||
|
||||
|
||||
# Export all results to json
|
||||
|
||||
import json
|
||||
|
||||
# Greedy
|
||||
greedy_results = {
|
||||
"greedy_times_coin_list": greedy_times_coin_list,
|
||||
"greedy_labels_coin_list": greedy_labels_coin_list,
|
||||
"greedy_times_amount_list": greedy_times_amount_list,
|
||||
"greedy_labels_amount_list": greedy_labels_amount_list
|
||||
}
|
||||
|
||||
with open('greedy_results.json', 'w') as fp:
|
||||
json.dump(greedy_results, fp)
|
||||
|
||||
# Iterative
|
||||
iterative_results = {
|
||||
"iterative_times_coin_list": iterative_times_coin_list,
|
||||
"iterative_labels_coin_list": iterative_labels_coin_list
|
||||
}
|
||||
|
||||
with open('iterative_results.json', 'w') as fp:
|
||||
json.dump(iterative_results, fp)
|
||||
|
||||
# Recursive
|
||||
recursive_results = {
|
||||
"recursive_times_coin_list": recursive_times_coin_list,
|
||||
"recursive_labels_coin_list": recursive_labels_coin_list
|
||||
}
|
||||
|
||||
with open('recursive_results.json', 'w') as fp:
|
||||
json.dump(recursive_results, fp)
|
||||
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
```python
|
||||
# Data
|
||||
amounts = [12.35, 12.35*2, 12.35*3, 12.35*4, 12.35*5, 12.35*6, 12.35*7, 12.35*8, 12.35*9, 12.35*10]
|
||||
|
||||
coin_list = [
|
||||
[0.2, 0.1, 0.05],
|
||||
[1, 0.2, 0.1, 0.05],
|
||||
[2, 1, 0.2, 0.1, 0.05],
|
||||
[5, 2, 1, 0.2, 0.1, 0.05],
|
||||
[5, 2, 1, 0.5, 0.2, 0.1, 0.05],
|
||||
[5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.02],
|
||||
[5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01]
|
||||
]
|
||||
|
||||
import time
|
||||
import matplotlib.pyplot as plt
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
# Greedy
|
||||
def greedy_make_change(amount, coins):
|
||||
i = 0
|
||||
change = []
|
||||
|
||||
while amount > 0 and len(coins) > i:
|
||||
# print(str(round(amount//coins[i])) + " Coins of " + str(coins[i]) + "€")
|
||||
for j in range(round(amount//coins[i])):
|
||||
change.append(coins[i])
|
||||
amount = round(amount%coins[i], 2)
|
||||
i = i+1
|
||||
if amount > 0:
|
||||
print(f"Cannot make exact change for {amount:.2f}€")
|
||||
return change
|
||||
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
# Iterative
|
||||
|
||||
# Function to count occurrences of items in a list
|
||||
def count_occurrences(array):
|
||||
counts = {}
|
||||
for item in array:
|
||||
if item in counts:
|
||||
counts[item] += 1 # If the item is already in the dictionary, increment its count
|
||||
else:
|
||||
counts[item] = 1 # If the item is not in the dictionary, add it with a count of 1
|
||||
return counts
|
||||
|
||||
# Function to calculate all combinations of coins to make a specific amount
|
||||
def calculate_change_combinations(amount, coins):
|
||||
# Convert euro amounts to cents for calculations
|
||||
amount_cents = int(amount * 100)
|
||||
coin_values_cents = [int(coin * 100) for coin in coins]
|
||||
|
||||
# Initialize a list to store combinations and their counts
|
||||
combinations = []
|
||||
stack = [(0, [], 0)] # (current amount in cents, current combination, current coin index)
|
||||
|
||||
while stack:
|
||||
current_amount, current_combination, current_coin_index = stack.pop()
|
||||
|
||||
# If the current combination sums up to the target amount, add it to the list
|
||||
if current_amount == amount_cents:
|
||||
combinations.append(current_combination)
|
||||
# If the current amount is less than the target amount and there are more coins to consider
|
||||
elif current_amount < amount_cents and current_coin_index < len(coin_values_cents):
|
||||
coin = coin_values_cents[current_coin_index]
|
||||
max_count = (amount_cents - current_amount) // coin # Maximum count of the current coin
|
||||
|
||||
# Try adding different counts of the current coin to explore possibilities
|
||||
for count in range(max_count + 1):
|
||||
new_amount = current_amount + count * coin
|
||||
new_combination = current_combination + [coins[current_coin_index]] * count
|
||||
# Push the new state onto the stack for further exploration
|
||||
stack.append((new_amount, new_combination, current_coin_index + 1))
|
||||
|
||||
# Print the total number of combinations
|
||||
# print(f"Total number of combinations: {len(combinations)}")
|
||||
return combinations
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
# Recursive
|
||||
def make_change_recursive(amount, coins, start, current_change, result):
|
||||
if amount == 0:
|
||||
result.append(current_change[:])
|
||||
return
|
||||
|
||||
for i in range(start, len(coins)):
|
||||
coin_cents = round(coins[i] * 100)
|
||||
if amount >= coin_cents:
|
||||
current_change.append(coins[i])
|
||||
make_change_recursive(amount - coin_cents, coins, i, current_change, result)
|
||||
current_change.pop()
|
||||
|
||||
return result
|
||||
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
greedy_times_coin_list = []
|
||||
greedy_labels_coin_list = []
|
||||
|
||||
coins_list_copy = coin_list.copy()
|
||||
|
||||
for coins in coins_list_copy:
|
||||
start = time.perf_counter()
|
||||
greedy_make_change(amounts[0], coins)
|
||||
end = time.perf_counter()
|
||||
greedy_times_coin_list.append(end - start)
|
||||
greedy_labels_coin_list.append(str(len(coins)))
|
||||
|
||||
greedy_times_amount_list = []
|
||||
greedy_labels_amount_list = []
|
||||
|
||||
# Plot a graph
|
||||
plt.figure(figsize=(10, 6))
|
||||
|
||||
# NO BAR
|
||||
plt.plot(greedy_labels_coin_list[1:], greedy_times_coin_list[1:], marker='o', linestyle='solid')
|
||||
plt.xlabel('Coins')
|
||||
plt.ylabel('Time (s)')
|
||||
plt.title('Greedy Algorithm using different coins to make change for 12.35€')
|
||||
|
||||
plt.show()
|
||||
|
||||
for amount in amounts:
|
||||
start = time.perf_counter()
|
||||
greedy_make_change(amount, coin_list[-1])
|
||||
end = time.perf_counter()
|
||||
greedy_times_amount_list.append(end - start)
|
||||
greedy_labels_amount_list.append(str(amount))
|
||||
|
||||
# NO BAR
|
||||
plt.plot(greedy_labels_amount_list[1:], greedy_times_amount_list[1:], marker='o', linestyle='solid')
|
||||
plt.xlabel('Amount')
|
||||
plt.ylabel('Time (s)')
|
||||
plt.title('Greedy Algorithm using 9 coins to make change for different amounts')
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
iterative_times_coin_list = []
|
||||
iterative_labels_coin_list = []
|
||||
|
||||
coins_list_copy = coin_list.copy()
|
||||
|
||||
for coins in coins_list_copy[:5]:
|
||||
start = time.perf_counter()
|
||||
calculate_change_combinations(amounts[0], coins)
|
||||
end = time.perf_counter()
|
||||
iterative_times_coin_list.append(end - start)
|
||||
iterative_labels_coin_list.append(str(len(coins)))
|
||||
|
||||
# Plot a graph
|
||||
plt.figure(figsize=(10, 6))
|
||||
|
||||
# NO BAR
|
||||
plt.plot(iterative_labels_coin_list[1:], iterative_times_coin_list[1:], marker='o', linestyle='solid')
|
||||
plt.xlabel('Coins')
|
||||
plt.ylabel('Time (s)')
|
||||
plt.title('Iterative Algorithm generating all the possibilities using different coins to make change for 12.35€')
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
The complexity increase more when we add coins that are smaller. We can see that adding 0.02 and 0.01 have more impact than adding 2, and 5.
|
||||
|
||||
|
||||
```python
|
||||
recursive_times_coin_list = []
|
||||
recursive_labels_coin_list = []
|
||||
|
||||
coins_list_copy = coin_list.copy()
|
||||
|
||||
for coins in coins_list_copy[:5]:
|
||||
# print(coins)
|
||||
start = time.perf_counter()
|
||||
make_change_recursive(amounts[0]*100, coins, 0, [], [])
|
||||
end = time.perf_counter()
|
||||
recursive_times_coin_list.append(end - start)
|
||||
recursive_labels_coin_list.append(str(len(coins)))
|
||||
|
||||
# Plot a graph
|
||||
plt.figure(figsize=(10, 6))
|
||||
|
||||
# NO BAR
|
||||
plt.plot(recursive_labels_coin_list[1:], recursive_times_coin_list[1:], marker='o', linestyle='solid')
|
||||
plt.xlabel('Coins')
|
||||
plt.ylabel('Time (s)')
|
||||
plt.title('Recursive Algorithm generating all the possibilities using different coins to make change for 12.35€')
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||
[0.2, 0.1, 0.05]
|
||||
[1, 0.2, 0.1, 0.05]
|
||||
[2, 1, 0.2, 0.1, 0.05]
|
||||
[5, 2, 1, 0.2, 0.1, 0.05]
|
||||
[5, 2, 1, 0.5, 0.2, 0.1, 0.05]
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
Complexity seems polynomial or exponential, but it is not possible to test using this problem as it takes to much time to run on all the list of coins.
|
||||
|
||||
|
||||
```python
|
||||
# Export all results to json
|
||||
|
||||
import json
|
||||
|
||||
# Greedy
|
||||
greedy_results = {
|
||||
"greedy_times_coin_list": greedy_times_coin_list,
|
||||
"greedy_labels_coin_list": greedy_labels_coin_list,
|
||||
"greedy_times_amount_list": greedy_times_amount_list,
|
||||
"greedy_labels_amount_list": greedy_labels_amount_list
|
||||
}
|
||||
|
||||
with open('greedy_results.json', 'w') as fp:
|
||||
json.dump(greedy_results, fp)
|
||||
|
||||
# Iterative
|
||||
iterative_results = {
|
||||
"iterative_times_coin_list": iterative_times_coin_list,
|
||||
"iterative_labels_coin_list": iterative_labels_coin_list
|
||||
}
|
||||
|
||||
with open('iterative_results.json', 'w') as fp:
|
||||
json.dump(iterative_results, fp)
|
||||
|
||||
# Recursive
|
||||
recursive_results = {
|
||||
"recursive_times_coin_list": recursive_times_coin_list,
|
||||
"recursive_labels_coin_list": recursive_labels_coin_list
|
||||
}
|
||||
|
||||
with open('recursive_results.json', 'w') as fp:
|
||||
json.dump(recursive_results, fp)
|
||||
|
||||
```
|
||||
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1 @@
|
||||
{"greedy_times_coin_list": [2.7266999495623168e-05, 4.290000106266234e-06, 4.249999619787559e-06, 4.71700059279101e-06, 5.1530005293898284e-06, 5.198000508244149e-06, 5.539999619941227e-06], "greedy_labels_coin_list": ["3", "4", "5", "6", "7", "8", "9"], "greedy_times_amount_list": [1.591299951542169e-05, 4.618999810190871e-06, 5.142000190971885e-06, 4.219999937049579e-06, 5.545999556488823e-06, 4.925000212097075e-06, 5.834999683429487e-06, 5.122999937157147e-06, 5.883000085304957e-06, 4.184000317764003e-06], "greedy_labels_amount_list": ["12.35", "24.7", "37.05", "49.4", "61.75", "74.1", "86.45", "98.8", "111.14999999999999", "123.5"]}
|
||||
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1 @@
|
||||
{"iterative_times_coin_list": [0.23977153500072745, 0.7593725659999109, 1.4812359939996895, 1.3827171279999675, 6.808871098000054], "iterative_labels_coin_list": ["3", "4", "5", "6", "7"]}
|
||||
|
After Width: | Height: | Size: 29 KiB |
@@ -0,0 +1 @@
|
||||
{"recursive_times_coin_list": [0.12517524299983052, 0.44915761500033113, 0.8362728540005264, 0.9114188160001504, 4.504991862000679], "recursive_labels_coin_list": ["3", "4", "5", "6", "7"]}
|
||||
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,188 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Node 0 : [5, 7]\n",
|
||||
"Node 1 : [7, 5]\n",
|
||||
"Node 2 : [3]\n",
|
||||
"Node 3 : [5, 6, 2, 7]\n",
|
||||
"Node 4 : [7, 8]\n",
|
||||
"Node 5 : [0, 7, 3, 1]\n",
|
||||
"Node 6 : [3]\n",
|
||||
"Node 7 : [0, 5, 3, 8, 1, 4]\n",
|
||||
"Node 8 : [7, 4]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Diapo 22\n",
|
||||
"\n",
|
||||
"N = [0,1,2,3,4,5,6,7,8]\n",
|
||||
"E = [(0,5), (0,7), (5,7), (5,3) , (3,6), (3,2), (7,3), (7,8), (7,1), (7,4), (4,8), (5,1)]\n",
|
||||
"\n",
|
||||
"# First, and for the sake of simplification, we will count the number of\n",
|
||||
"# segments / edges separating different points / nodes (of a graph)\n",
|
||||
"\n",
|
||||
"counted = [ [] for i in range(len(N))]\n",
|
||||
"\n",
|
||||
"for e in E:\n",
|
||||
" if e[0] != e[1]:\n",
|
||||
" if e[0] not in counted[e[1]]:\n",
|
||||
" counted[e[1]].append(e[0])\n",
|
||||
" if e[1] not in counted[e[0]]:\n",
|
||||
" counted[e[0]].append(e[1])\n",
|
||||
"\n",
|
||||
"# Print counted\n",
|
||||
"for i in range(len(N)):\n",
|
||||
" print(\"Node\", i, \":\", counted[i])\n",
|
||||
"\n",
|
||||
"# We want to write a program which, given a subset of these nodes\n",
|
||||
"# and edges, computes the list of nodes ordered in ascending order\n",
|
||||
"# of distances to a given particular node.\n",
|
||||
"# » For node 0 and the subset N0 = {1,3,4,5,7} and the associated edges E0 = {(0,5),\n",
|
||||
"# (0,7), (5,7), (7, 3), (7,1), (7,4), (5,1)}\n",
|
||||
"# and the expected result is L0 = {0,5,7,1,3,4}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[0, 0, 0, 0, 0, 1, 0, 1, 0]\n",
|
||||
"[0, 0, 0, 0, 0, 1, 0, 1, 0]\n",
|
||||
"[0, 0, 0, 1, 0, 0, 0, 0, 0]\n",
|
||||
"[0, 0, 1, 0, 0, 1, 1, 1, 0]\n",
|
||||
"[0, 0, 0, 0, 0, 0, 0, 1, 1]\n",
|
||||
"[1, 1, 0, 1, 0, 0, 0, 1, 0]\n",
|
||||
"[0, 0, 0, 1, 0, 0, 0, 0, 0]\n",
|
||||
"[1, 1, 0, 1, 1, 1, 0, 0, 1]\n",
|
||||
"[0, 0, 0, 0, 1, 0, 0, 1, 0]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Generate matrix from nodes and edges\n",
|
||||
"def graph_to_matrix(N, E):\n",
|
||||
" M = [ [0 for i in range(len(N))] for j in range(len(N))]\n",
|
||||
" for e in E:\n",
|
||||
" M[e[0]][e[1]] = 1\n",
|
||||
" M[e[1]][e[0]] = 1\n",
|
||||
" return M\n",
|
||||
"\n",
|
||||
"# Print matrix\n",
|
||||
"matrix = graph_to_matrix(N, E)\n",
|
||||
"for i in range(len(N)):\n",
|
||||
" print(matrix[i])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def transitive_closure(matrix):\n",
|
||||
" modification = True\n",
|
||||
" while modification:\n",
|
||||
" modification = False\n",
|
||||
" for i in range(len(matrix)):\n",
|
||||
" for j in range(len(matrix)):\n",
|
||||
" for k in range(len(matrix)):\n",
|
||||
" if matrix[i][j] > 0 and matrix[j][k] > 0 and matrix[i][k] == 0:\n",
|
||||
" matrix[i][k] = matrix[i][j] + matrix[j][k]\n",
|
||||
" modification = True\n",
|
||||
" else:\n",
|
||||
" modification = False\n",
|
||||
"\n",
|
||||
" return matrix\n",
|
||||
"\n",
|
||||
"def compute(node, subset, edges):\n",
|
||||
" linked_edges = []\n",
|
||||
"\n",
|
||||
" for edge in edges:\n",
|
||||
" # Get only the edges linked to the node or between the node of the subset\n",
|
||||
" if edge[0] == node or edge[1] == node:\n",
|
||||
" linked_edges.append(edge)\n",
|
||||
" elif edge[0] in subset and edge[1] in subset:\n",
|
||||
" linked_edges.append(edge)\n",
|
||||
"\n",
|
||||
" # Generate matrix from nodes and edges\n",
|
||||
" matrix = graph_to_matrix(subset + [node], linked_edges)\n",
|
||||
" matrix = transitive_closure(matrix)\n",
|
||||
"\n",
|
||||
" # Make symmetrical\n",
|
||||
" for i in range(len(matrix)):\n",
|
||||
" for j in range(len(matrix)):\n",
|
||||
" if matrix[i][j] > 0:\n",
|
||||
" matrix[j][i] = matrix[i][j]\n",
|
||||
"\n",
|
||||
" # Compute the distance between the node and each other\n",
|
||||
" \n",
|
||||
" distances = [0 for i in range(len(subset))]\n",
|
||||
" for i in range(len(subset+1)):\n",
|
||||
" if(i == node):\n",
|
||||
" distances[i] = (0, 0)\n",
|
||||
" distances[i] = (i, matrix[node][i]) \n",
|
||||
"\n",
|
||||
" # Sort the distances\n",
|
||||
" distances.sort(key=lambda x: x[1])\n",
|
||||
"\n",
|
||||
" # Return the sorted list of nodes\n",
|
||||
" return [x[0] for x in distances]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[(0, 5), (0, 7), (5, 7), (5, 3), (7, 3), (7, 1), (7, 4), (5, 1)]"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"compute(0, [1,3,4,5,7], E)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
```python
|
||||
# Diapo 22
|
||||
|
||||
N = [0,1,2,3,4,5,6,7,8]
|
||||
E = [(0,5), (0,7), (5,7), (5,3) , (3,6), (3,2), (7,3), (7,8), (7,1), (7,4), (4,8), (5,1)]
|
||||
|
||||
# First, and for the sake of simplification, we will count the number of
|
||||
# segments / edges separating different points / nodes (of a graph)
|
||||
|
||||
counted = [ [] for i in range(len(N))]
|
||||
|
||||
for e in E:
|
||||
if e[0] != e[1]:
|
||||
if e[0] not in counted[e[1]]:
|
||||
counted[e[1]].append(e[0])
|
||||
if e[1] not in counted[e[0]]:
|
||||
counted[e[0]].append(e[1])
|
||||
|
||||
# Print counted
|
||||
for i in range(len(N)):
|
||||
print("Node", i, ":", counted[i])
|
||||
|
||||
# We want to write a program which, given a subset of these nodes
|
||||
# and edges, computes the list of nodes ordered in ascending order
|
||||
# of distances to a given particular node.
|
||||
# » For node 0 and the subset N0 = {1,3,4,5,7} and the associated edges E0 = {(0,5),
|
||||
# (0,7), (5,7), (7, 3), (7,1), (7,4), (5,1)}
|
||||
# and the expected result is L0 = {0,5,7,1,3,4}
|
||||
```
|
||||
|
||||
Node 0 : [5, 7]
|
||||
Node 1 : [7, 5]
|
||||
Node 2 : [3]
|
||||
Node 3 : [5, 6, 2, 7]
|
||||
Node 4 : [7, 8]
|
||||
Node 5 : [0, 7, 3, 1]
|
||||
Node 6 : [3]
|
||||
Node 7 : [0, 5, 3, 8, 1, 4]
|
||||
Node 8 : [7, 4]
|
||||
|
||||
|
||||
|
||||
```python
|
||||
# Generate matrix from nodes and edges
|
||||
def graph_to_matrix(N, E):
|
||||
M = [ [0 for i in range(len(N))] for j in range(len(N))]
|
||||
for e in E:
|
||||
M[e[0]][e[1]] = 1
|
||||
M[e[1]][e[0]] = 1
|
||||
return M
|
||||
|
||||
# Print matrix
|
||||
matrix = graph_to_matrix(N, E)
|
||||
for i in range(len(N)):
|
||||
print(matrix[i])
|
||||
```
|
||||
|
||||
[0, 0, 0, 0, 0, 1, 0, 1, 0]
|
||||
[0, 0, 0, 0, 0, 1, 0, 1, 0]
|
||||
[0, 0, 0, 1, 0, 0, 0, 0, 0]
|
||||
[0, 0, 1, 0, 0, 1, 1, 1, 0]
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1]
|
||||
[1, 1, 0, 1, 0, 0, 0, 1, 0]
|
||||
[0, 0, 0, 1, 0, 0, 0, 0, 0]
|
||||
[1, 1, 0, 1, 1, 1, 0, 0, 1]
|
||||
[0, 0, 0, 0, 1, 0, 0, 1, 0]
|
||||
|
||||
|
||||
|
||||
```python
|
||||
def transitive_closure(matrix):
|
||||
modification = True
|
||||
while modification:
|
||||
modification = False
|
||||
for i in range(len(matrix)):
|
||||
for j in range(len(matrix)):
|
||||
for k in range(len(matrix)):
|
||||
if matrix[i][j] > 0 and matrix[j][k] > 0 and matrix[i][k] == 0:
|
||||
matrix[i][k] = matrix[i][j] + matrix[j][k]
|
||||
modification = True
|
||||
else:
|
||||
modification = False
|
||||
|
||||
return matrix
|
||||
|
||||
def compute(node, subset, edges):
|
||||
linked_edges = []
|
||||
|
||||
for edge in edges:
|
||||
# Get only the edges linked to the node or between the node of the subset
|
||||
if edge[0] == node or edge[1] == node:
|
||||
linked_edges.append(edge)
|
||||
elif edge[0] in subset and edge[1] in subset:
|
||||
linked_edges.append(edge)
|
||||
|
||||
# Generate matrix from nodes and edges
|
||||
matrix = graph_to_matrix(subset + [node], linked_edges)
|
||||
matrix = transitive_closure(matrix)
|
||||
|
||||
# Make symmetrical
|
||||
for i in range(len(matrix)):
|
||||
for j in range(len(matrix)):
|
||||
if matrix[i][j] > 0:
|
||||
matrix[j][i] = matrix[i][j]
|
||||
|
||||
# Compute the distance between the node and each other
|
||||
|
||||
distances = [0 for i in range(len(subset))]
|
||||
for i in range(len(subset+1)):
|
||||
if(i == node):
|
||||
distances[i] = (0, 0)
|
||||
distances[i] = (i, matrix[node][i])
|
||||
|
||||
# Sort the distances
|
||||
distances.sort(key=lambda x: x[1])
|
||||
|
||||
# Return the sorted list of nodes
|
||||
return [x[0] for x in distances]
|
||||
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
compute(0, [1,3,4,5,7], E)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
[(0, 5), (0, 7), (5, 7), (5, 3), (7, 3), (7, 1), (7, 4), (5, 1)]
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# In[4]:
|
||||
|
||||
|
||||
# Diapo 22
|
||||
|
||||
N = [0,1,2,3,4,5,6,7,8]
|
||||
E = [(0,5), (0,7), (5,7), (5,3) , (3,6), (3,2), (7,3), (7,8), (7,1), (7,4), (4,8), (5,1)]
|
||||
|
||||
# First, and for the sake of simplification, we will count the number of
|
||||
# segments / edges separating different points / nodes (of a graph)
|
||||
|
||||
counted = [ [] for i in range(len(N))]
|
||||
|
||||
for e in E:
|
||||
if e[0] != e[1]:
|
||||
if e[0] not in counted[e[1]]:
|
||||
counted[e[1]].append(e[0])
|
||||
if e[1] not in counted[e[0]]:
|
||||
counted[e[0]].append(e[1])
|
||||
|
||||
# Print counted
|
||||
for i in range(len(N)):
|
||||
print("Node", i, ":", counted[i])
|
||||
|
||||
# We want to write a program which, given a subset of these nodes
|
||||
# and edges, computes the list of nodes ordered in ascending order
|
||||
# of distances to a given particular node.
|
||||
# » For node 0 and the subset N0 = {1,3,4,5,7} and the associated edges E0 = {(0,5),
|
||||
# (0,7), (5,7), (7, 3), (7,1), (7,4), (5,1)}
|
||||
# and the expected result is L0 = {0,5,7,1,3,4}
|
||||
|
||||
|
||||
# In[9]:
|
||||
|
||||
|
||||
# Generate matrix from nodes and edges
|
||||
def graph_to_matrix(N, E):
|
||||
M = [ [0 for i in range(len(N))] for j in range(len(N))]
|
||||
for e in E:
|
||||
M[e[0]][e[1]] = 1
|
||||
M[e[1]][e[0]] = 1
|
||||
return M
|
||||
|
||||
# Print matrix
|
||||
matrix = graph_to_matrix(N, E)
|
||||
for i in range(len(N)):
|
||||
print(matrix[i])
|
||||
|
||||
|
||||
# In[6]:
|
||||
|
||||
|
||||
def transitive_closure(matrix):
|
||||
modification = True
|
||||
while modification:
|
||||
modification = False
|
||||
for i in range(len(matrix)):
|
||||
for j in range(len(matrix)):
|
||||
for k in range(len(matrix)):
|
||||
if matrix[i][j] > 0 and matrix[j][k] > 0 and matrix[i][k] == 0:
|
||||
matrix[i][k] = matrix[i][j] + matrix[j][k]
|
||||
modification = True
|
||||
else:
|
||||
modification = False
|
||||
|
||||
return matrix
|
||||
|
||||
def compute(node, subset, edges):
|
||||
linked_edges = []
|
||||
|
||||
for edge in edges:
|
||||
# Get only the edges linked to the node or between the node of the subset
|
||||
if edge[0] == node or edge[1] == node:
|
||||
linked_edges.append(edge)
|
||||
elif edge[0] in subset and edge[1] in subset:
|
||||
linked_edges.append(edge)
|
||||
|
||||
# Generate matrix from nodes and edges
|
||||
matrix = graph_to_matrix(subset + [node], linked_edges)
|
||||
matrix = transitive_closure(matrix)
|
||||
|
||||
# Make symmetrical
|
||||
for i in range(len(matrix)):
|
||||
for j in range(len(matrix)):
|
||||
if matrix[i][j] > 0:
|
||||
matrix[j][i] = matrix[i][j]
|
||||
|
||||
# Compute the distance between the node and each other
|
||||
|
||||
distances = [0 for i in range(len(subset))]
|
||||
for i in range(len(subset+1)):
|
||||
if(i == node):
|
||||
distances[i] = (0, 0)
|
||||
distances[i] = (i, matrix[node][i])
|
||||
|
||||
# Sort the distances
|
||||
distances.sort(key=lambda x: x[1])
|
||||
|
||||
# Return the sorted list of nodes
|
||||
return [x[0] for x in distances]
|
||||
|
||||
|
||||
# In[7]:
|
||||
|
||||
|
||||
compute(0, [1,3,4,5,7], E)
|
||||
|
||||