Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ Fun with algorithms and data structures

Have fun, learn about algorithms and data structures and build the fastest and cleanest algorithms from far west.


## Local Development

Though, it's not a requirement, we encourage the usage of `pyenv`, a simple Python version management program, to select the right Python version (>=3.8,<4.0). You can follow the [Windows](https://github.com/pyenv-win/pyenv-win#installation) or the [macOS instructions](https://github.com/pyenv/pyenv#installation), both found in the official `pyenv` repository. Installation on Linux based systems is not so straightforward but it is also doable.
Expand Down
12 changes: 4 additions & 8 deletions solutions/greedy/fractional_knapsack/item.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
from dataclasses import dataclass
from typing import List


@dataclass
class Item:
value: float
weight: int

@property
def fractional_value(self):
return self.value / self.weight
def __init__(self, value: float, weight: int) -> None:
self.value: float
self.weight: int
self.fractional_value: float = value / weight

def __gt__(self, other):
if not self._is_valid_operand(other):
Expand Down
11 changes: 11 additions & 0 deletions solutions/greedy/minimum_refueling_stops/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from solutions.greedy.minimum_refueling_stops.brute_force_solution import (
brute_force_solution,
)
from solutions.greedy.minimum_refueling_stops.efficient_solution import (
efficient_solution,
)

__all__ = [
"brute_force_solution",
"efficient_solution",
]
32 changes: 32 additions & 0 deletions solutions/greedy/minimum_refueling_stops/brute_force_solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import List, Tuple


def brute_force_solution(
input_min_refueling_stops: Tuple[float, float, List[float]]
) -> int:
"""Solve the problem using brute force. The brute force solution is consists on a
nested loop, hence it is O(n^2).

Parameters
----------
input_min_refueling_stops : Tuple[float, float, List[int]]
The input data, a tuple of the form (distance, tank_capacity, stations).

Returns
-------
int
The minimum number of refueling stops.
"""
destination_distance, tank_capacity, gas_stations = input_min_refueling_stops
stops = [0.0] + gas_stations + [destination_distance]
refuel = 0
i, j = 0, 0
while i + 2 < len(stops):
i += j
for j, next_gas_station in enumerate(stops[(i + 1) :]):
if tank_capacity < next_gas_station - stops[i]:
if j == 0:
return -1
refuel += 1
break
return refuel
33 changes: 33 additions & 0 deletions solutions/greedy/minimum_refueling_stops/efficient_solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from typing import List, Tuple


def efficient_solution(
input_min_refueling_stops: Tuple[float, float, List[float]]
) -> int:
"""Solve the problem using a greedy solution. The greedy solution is consists on
refueling at the last possible station that the tank deposit capacity allows, then
removing the previous stations and start again. This is O(1) in time and O(n) in
space.

Parameters
----------
input_min_refueling_stops : Tuple[float, float, List[float]]
The input data, a tuple of the form (distance, tank_capacity, stations).

Returns
-------
int
The minimum number of refueling stops.
"""
destination_distance, tank_capacity, gas_stations = input_min_refueling_stops
stops = [0.0] + gas_stations + [destination_distance]
refueling, i = 0, 0
while i < len(stops) - 1:
i += 1
if tank_capacity < stops[i] - stops[0]:
if i == 1:
return -1
refueling += 1
stops = stops[i - 1 :]
i = 0
return refueling
36 changes: 36 additions & 0 deletions tests/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,39 @@ fractional_knapsack:
repeat_performance_tests_per_input: 3
number_performance_tests_per_input: 5
times_faster_efficient_to_brute_force: 1000
minimum_refueling_stops:
stress_tests:
small_tests:
max_fuel_stops: 100
min_fuel_stops: 2
max_distance_between_stops: 50
min_distance_between_stops: 10
max_tank_capacity: 100
min_tank_capacity: 20
max_distance_to_destination_from_last_stop: 100
min_distance_to_destination_from_last_stop: 50
test_iterations: 10
big_tests:
max_fuel_stops: 1000
min_fuel_stops: 200
max_distance_between_stops: 50
min_distance_between_stops: 10
max_tank_capacity: 100
min_tank_capacity: 20
max_distance_to_destination_from_last_stop: 100
min_distance_to_destination_from_last_stop: 50
test_iterations: 100
performance:
max_fuel_stops: 250000
min_fuel_stops: 240000
max_distance_between_stops: 50
min_distance_between_stops: 10
max_tank_capacity: 100
min_tank_capacity: 20
max_distance_to_destination_from_last_stop: 100
min_distance_to_destination_from_last_stop: 50
test_iterations: 1
performance_tests:
repeat_performance_tests_per_input: 2
number_performance_tests_per_input: 3
times_faster_efficient_to_brute_force: 1.05
11 changes: 7 additions & 4 deletions tests/greedy/fractional_knapsack/test_efficient_solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ def sample_input_fractional_knapsack(
@pytest.mark.parametrize(
"input_fractional_knapsack", sample_input_fractional_knapsack(small_tests_params)
)
def test_stress_tests_efficient_solution_small_stress(input_fractional_knapsack):
def test_stress_tests_efficient_solution_small_stress(
input_fractional_knapsack: Tuple[int, int, List[Item]]
) -> None:
input_fractional_knapsack_copy = copy.deepcopy(input_fractional_knapsack)
brute_force = fractional_knapsack.brute_force_solution(
input_fractional_knapsack_copy
Expand All @@ -83,7 +85,9 @@ def test_stress_tests_efficient_solution_small_stress(input_fractional_knapsack)
"input_fractional_knapsack",
sample_input_fractional_knapsack(big_tests_params),
)
def test_stress_tests_efficient_solution_big_stress(input_fractional_knapsack):
def test_stress_tests_efficient_solution_big_stress(
input_fractional_knapsack: Tuple[int, int, List[Item]]
) -> None:
input_fractional_knapsack_copy = copy.deepcopy(input_fractional_knapsack)
efficient = fractional_knapsack.efficient_solution(input_fractional_knapsack)
brute_force = fractional_knapsack.brute_force_solution(
Expand All @@ -100,7 +104,7 @@ def test_stress_tests_efficient_solution_big_stress(input_fractional_knapsack):
@pytest.mark.parametrize(
"input_fractional_knapsack", sample_input_fractional_knapsack(performance_params)
)
def test_performance(input_fractional_knapsack) -> None:
def test_performance(input_fractional_knapsack: Tuple[int, int, List[Item]]) -> None:
config_names = ["fractional_knapsack", "performance_tests"]
performance_measures = read_config(PerformanceMeasures, config_names)
input_fractional_knapsack_copy = copy.deepcopy(input_fractional_knapsack)
Expand All @@ -113,5 +117,4 @@ def test_performance(input_fractional_knapsack) -> None:
input_fractional_knapsack,
)
times_faster = performance_measures.times_faster_efficient_to_brute_force

assert times_faster * efficient_time < brute_force_time
Empty file.
17 changes: 17 additions & 0 deletions tests/greedy/minimum_refueling_stops/stress_tests_params.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from dataclasses import dataclass

from tests.read_config import Config


@dataclass
class StressTestsParams(Config):
max_fuel_stops: int
min_fuel_stops: int
max_distance_between_stops: int
min_distance_between_stops: int
max_tank_capacity: int
min_tank_capacity: int
max_distance_to_destination_from_last_stop: int
min_distance_to_destination_from_last_stop: int
test_iterations: int
seed: int = 123
10 changes: 10 additions & 0 deletions tests/greedy/minimum_refueling_stops/test_brute_force_solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from solutions.greedy import minimum_refueling_stops


def test_brute_force_solution():
assert 2 == minimum_refueling_stops.brute_force_solution(
(950, 400, [200, 375, 550, 750])
)

assert -1 == minimum_refueling_stops.brute_force_solution((10, 3, [1, 2, 5, 9]))
assert 0 == minimum_refueling_stops.brute_force_solution((200, 250, [100, 150]))
130 changes: 130 additions & 0 deletions tests/greedy/minimum_refueling_stops/test_efficient_solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import random
from typing import List, Tuple

import pytest

from solutions.greedy import minimum_refueling_stops
from tests.greedy.minimum_refueling_stops.stress_tests_params import StressTestsParams
from tests.performance_measures import PerformanceMeasures
from tests.read_config import get_names, read_config


def test_efficient_solution_simple_cases() -> None:
assert 2 == minimum_refueling_stops.efficient_solution(
(950, 400, [200, 375, 550, 750])
)

assert -1 == minimum_refueling_stops.efficient_solution((10, 3, [1, 2, 5, 9]))
assert 0 == minimum_refueling_stops.efficient_solution((200, 250, [100, 150]))


#
# get test parameters
#


names = ["minimum_refueling_stops", "stress_tests"]
small_tests_params = read_config(StressTestsParams, get_names(names, "small_tests"))
big_tests_params = read_config(StressTestsParams, get_names(names, "big_tests"))
performance_params = read_config(StressTestsParams, get_names(names, "performance"))

#
# parametrize test inputs
#


def _cumsum(x: List[int]) -> List[int]:
res = []
elements_sum = 0
for element in x:
elements_sum += element
res.append(elements_sum)
return res


def sample_input_minimum_refueling_stops(
params: StressTestsParams,
) -> List[Tuple[int, int, List[int]]]:
random.seed(params.seed)
test_cases = []
for _ in range(params.test_iterations):
number_stops = random.randint(params.min_fuel_stops, params.max_fuel_stops)
min_distance = params.min_distance_between_stops
max_distance = params.max_distance_between_stops
stops = _cumsum(
[random.randint(min_distance, max_distance) for _ in range(number_stops)]
)
distance_to_destination = (
random.randint(
params.min_distance_to_destination_from_last_stop,
params.max_distance_to_destination_from_last_stop,
)
+ stops[-1]
)
tank_capacity = random.randint(
params.min_tank_capacity, params.max_tank_capacity
)
test_cases.append((distance_to_destination, tank_capacity, stops))
return test_cases


#
# stress tests
#


@pytest.mark.parametrize(
"input_minimum_refueling_stops",
sample_input_minimum_refueling_stops(small_tests_params),
)
def test_stress_tests_efficient_solution_small_stress(
input_minimum_refueling_stops: Tuple[float, float, List[float]]
) -> None:
brute_force = minimum_refueling_stops.brute_force_solution(
input_minimum_refueling_stops
)
efficient = minimum_refueling_stops.efficient_solution(
input_minimum_refueling_stops
)
assert brute_force == efficient


@pytest.mark.parametrize(
"input_minimum_refueling_stops",
sample_input_minimum_refueling_stops(big_tests_params),
)
def test_stress_tests_efficient_solution_big_stress(
input_minimum_refueling_stops: Tuple[float, float, List[float]]
) -> None:
brute_force = minimum_refueling_stops.brute_force_solution(
input_minimum_refueling_stops
)
efficient = minimum_refueling_stops.efficient_solution(
input_minimum_refueling_stops
)
assert brute_force == efficient


#
# performance tests
#


@pytest.mark.parametrize(
"input_minimum_refueling_stops",
sample_input_minimum_refueling_stops(performance_params),
)
def test_performance(input_minimum_refueling_stops: Tuple[int, int, List[int]]) -> None:
config_names = ["minimum_refueling_stops", "performance_tests"]
performance_measures = read_config(PerformanceMeasures, config_names)
brute_force_time = performance_measures.measure_performance(
minimum_refueling_stops.brute_force_solution,
input_minimum_refueling_stops,
)
efficient_time = performance_measures.measure_performance(
minimum_refueling_stops.efficient_solution,
input_minimum_refueling_stops,
)

times_faster = performance_measures.times_faster_efficient_to_brute_force
assert times_faster * efficient_time < brute_force_time