From c2e476e03726b8251529f260df27050ebebe788a Mon Sep 17 00:00:00 2001 From: Saifeddine75 Date: Sat, 10 May 2025 17:20:44 +0200 Subject: [PATCH 1/3] Add gitignore --- .gitignore | 5 +++++ conftest.py | 20 ++++++++++++++++++++ test_functions.py | 1 + utils.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 .gitignore create mode 100644 conftest.py create mode 100644 test_functions.py create mode 100644 utils.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..11764df --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Cache +__pycache__/ + +# IDE +.vscode/settings.json diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..0c2ebc2 --- /dev/null +++ b/conftest.py @@ -0,0 +1,20 @@ +import pytest +from utils import load_prefixed_functions, profile_function + +@pytest.fixture +def load_func(): + """Load function from current directory files prefixed with defined prefix + + for example to use function_a, you would type: load_func("function_a") + """ + def _load(prefix: str): + return load_prefixed_functions(prefix) + return _load + + +@pytest.fixture +def profile(): + """Profile function and return reports""" + def _runner(func, *args, **kwargs): + return profile_function(func, *args, **kwargs) + return _runner diff --git a/test_functions.py b/test_functions.py new file mode 100644 index 0000000..bb377e8 --- /dev/null +++ b/test_functions.py @@ -0,0 +1 @@ +import pytest \ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..16aa099 --- /dev/null +++ b/utils.py @@ -0,0 +1,33 @@ +import os +import io +import pstats +import cProfile +from importlib import import_module + + +def load_prefixed_functions(prefix: str): + """Import problem function resolver identified by file prefix""" + funcs = {} + for f in os.listdir("."): + if f.startswith(prefix) and f.endswith(".py"): + mod = import_module(f[:-3]) + func_name = f[5:-3] # strip "func_" and ".py" + funcs[func_name] = getattr(mod, func_name) + return funcs + + +def profile_function(func, *args, **kwargs): + """Show function execution time and cProfile detail reports""" + pr = cProfile.Profile() + pr.enable() + result = func(*args, **kwargs) + pr.disable() + + s = io.StringIO() + sortby = 'tottime' + ps = pstats.Stats(pr, stream=s).sort_stats(sortby) + ps.print_stats() + print(s.getvalue()) + + return result + From f2759604015a163e12e501af1de68a17f04ad8a6 Mon Sep 17 00:00:00 2001 From: Saifeddine75 <31001676+Saifeddine75@users.noreply.github.com> Date: Sat, 10 May 2025 20:41:51 +0200 Subject: [PATCH 2/3] Find the maximum average sum of sub array (#6) --- max_avg_sub_array.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 max_avg_sub_array.py diff --git a/max_avg_sub_array.py b/max_avg_sub_array.py new file mode 100644 index 0000000..f3c1fc1 --- /dev/null +++ b/max_avg_sub_array.py @@ -0,0 +1,29 @@ +""" +Given an array we want to find the maximum average of a sub array of size k. +We can do this in O(n) time using a sliding window approach. +""" + +import timeit +import cProfile +import random + + +def max_avg_sub_array(arr: list[int], k: int=10): + n = len(arr) + window_sum = sum(arr[:k]) + max_avg = window_sum / k + + for i in range(k, n): + window_sum += arr[i] - arr[i - k] + max_avg = max(max_avg, window_sum / k) + + return max_avg + + +random_array = [random.randint(-100000, 100000) for _ in range(100000)] +time_a = timeit.timeit(lambda: max_avg_sub_array(random_array), number=10) + +print(f"Function time: {time_a:.6f} seconds") + +print("\nProfiling Function:") +cProfile.run("max_avg_sub_array(random_array)") From c9f22e2f836a75811439b8ea2752b59ba227aaa1 Mon Sep 17 00:00:00 2001 From: Saifeddine Date: Sat, 10 May 2025 21:37:33 +0200 Subject: [PATCH 3/3] Merge list nodes by ascending order value --- data_structure.py | 35 +++++++++++++++++++++++++++++++++++ merge_list_nodes.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 data_structure.py create mode 100644 merge_list_nodes.py diff --git a/data_structure.py b/data_structure.py new file mode 100644 index 0000000..6552b3d --- /dev/null +++ b/data_structure.py @@ -0,0 +1,35 @@ +import random + + +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + + @classmethod + def from_list(cls, values): + """Builds a linked list from a list of values and returns the head.""" + if not values: + return None + head = cls(values[0]) + current = head + for val in values[1:]: + current.next = cls(val) + current = current.next + return head + + @classmethod + def generate_random(cls, count=10, low=1, high=100): + """Generate a linked list of random, unique integers.""" + if high - low + 1 < count: + raise ValueError("Range too small for the requested number of unique values.") + values = random.sample(range(low, high + 1), count) + return cls.from_list(sorted(values)) + + def print_list(self): + """Prints the linked list starting from the current node.""" + current = self + while current: + print(current.val, end=" -> ") + current = current.next + print("None") diff --git a/merge_list_nodes.py b/merge_list_nodes.py new file mode 100644 index 0000000..0851a99 --- /dev/null +++ b/merge_list_nodes.py @@ -0,0 +1,31 @@ +""" +Given 2 lists of nodes in ascending order, merge them into a single list in ascending order. +""" + +import timeit +from struct import ListNode + +POW = 5 + +def merge_sorted_lists(list1, list2): + """Merge two sorted lists into a single sorted list.""" + + dummy = ListNode(val=-1) + curr = dummy + + while list1 and list2: + if list1.val < list2.val: + curr.next = list1 + list1 = list1.next + else: + curr.next = list2 + list2 = list2.next + + curr.next = list1 if list1 else list2 + return dummy.next + +list1 = ListNode.generate_random(count=10**POW, low=1, high=99**POW) +list2 = ListNode.generate_random(count=10**POW, low=101**POW, high=199**POW) + +time_a = timeit.timeit(lambda: merge_sorted_lists(list1, list2), number=100) +print(f"Function time: {time_a:.6f} seconds")