|
| 1 | +# Greedy Algorithms |
| 2 | + |
| 3 | +Greedy algorithms are simple, intuitive algorithms that make a sequence of choices at each step with the hope of finding a global optimum. They are called "greedy" because at each step, they choose the most advantageous option without considering the future consequences. Despite their simplicity, greedy algorithms are powerful tools for solving optimization problems, especially when the problem exhibits the greedy-choice property. |
| 4 | + |
| 5 | +## Real-Life Examples of Greedy Algorithms |
| 6 | +- **Coin Change:** Finding the minimum number of coins to make a certain amount of change. |
| 7 | +- **Job Scheduling:** Assigning tasks to machines to minimize completion time. |
| 8 | +- **Huffman Coding:** Constructing an optimal prefix-free binary code for data compression. |
| 9 | +- **Fractional Knapsack:** Selecting items to maximize the value within a weight limit. |
| 10 | + |
| 11 | +# Some Common Greedy Algorithms |
| 12 | + |
| 13 | +# 1. Coin Change Problem |
| 14 | + |
| 15 | +The coin change problem is a classic example of a greedy algorithm. Given a set of coin denominations and a target amount, the objective is to find the minimum number of coins required to make up that amount. |
| 16 | + |
| 17 | +**Algorithm Overview:** |
| 18 | +- **Greedy Strategy:** At each step, the algorithm selects the largest denomination coin that is less than or equal to the remaining amount. |
| 19 | +- **Repeat Until Amount is Zero:** The process continues until the remaining amount becomes zero. |
| 20 | + |
| 21 | +## Coin Change Code in Python |
| 22 | + |
| 23 | +```python |
| 24 | +def coin_change(coins, amount): |
| 25 | + coins.sort(reverse=True) |
| 26 | + num_coins = 0 |
| 27 | + for coin in coins: |
| 28 | + num_coins += amount // coin |
| 29 | + amount %= coin |
| 30 | + if amount == 0: |
| 31 | + return num_coins |
| 32 | + else: |
| 33 | + return -1 |
| 34 | + |
| 35 | +coins = [1, 5, 10, 25] |
| 36 | +amount = 63 |
| 37 | +result = coin_change(coins, amount) |
| 38 | +if result != -1: |
| 39 | + print(f"Minimum number of coins required: {result}.") |
| 40 | +else: |
| 41 | + print("It is not possible to make the amount with the given denominations.") |
| 42 | +``` |
| 43 | + |
| 44 | +## Complexity Analysis |
| 45 | +- **Time Complexity**: O(n log n) for sorting (if not pre-sorted), O(n) for iteration |
| 46 | +- **Space Complexity**: O(1) |
| 47 | + |
| 48 | +</br> |
| 49 | +<hr> |
| 50 | +</br> |
| 51 | + |
| 52 | +# 2. Activity Selection Problem |
| 53 | + |
| 54 | +The activity selection problem involves selecting the maximum number of mutually compatible activities that can be performed by a single person or machine, assuming that a person can only work on one activity at a time. |
| 55 | + |
| 56 | +**Algorithm Overview:** |
| 57 | +- **Greedy Strategy:** Sort the activities based on their finish times. |
| 58 | +- **Selecting Activities:** Iterate through the sorted activities, selecting each activity if it doesn't conflict with the previously selected ones. |
| 59 | + |
| 60 | +## Activity Selection Code in Python |
| 61 | + |
| 62 | +```python |
| 63 | +def activity_selection(start, finish): |
| 64 | + n = len(start) |
| 65 | + activities = [] |
| 66 | + i = 0 |
| 67 | + activities.append(i) |
| 68 | + for j in range(1, n): |
| 69 | + if start[j] >= finish[i]: |
| 70 | + activities.append(j) |
| 71 | + i = j |
| 72 | + return activities |
| 73 | + |
| 74 | +start = [1, 3, 0, 5, 8, 5] |
| 75 | +finish = [2, 4, 6, 7, 9, 9] |
| 76 | +selected_activities = activity_selection(start, finish) |
| 77 | +print("Selected activities:", selected_activities) |
| 78 | +``` |
| 79 | + |
| 80 | +## Complexity Analysis |
| 81 | +- **Time Complexity**: O(n log n) for sorting (if not pre-sorted), O(n) for iteration |
| 82 | +- **Space Complexity**: O(1) |
| 83 | + |
| 84 | +</br> |
| 85 | +<hr> |
| 86 | +</br> |
| 87 | + |
| 88 | +# 3. Huffman Coding |
| 89 | + |
| 90 | +Huffman coding is a method of lossless data compression that efficiently represents characters or symbols in a file. It uses variable-length codes to represent characters, with shorter codes assigned to more frequent characters. |
| 91 | + |
| 92 | +**Algorithm Overview:** |
| 93 | +- **Frequency Analysis:** Determine the frequency of each character in the input data. |
| 94 | +- **Building the Huffman Tree:** Construct a binary tree where each leaf node represents a character and the path to the leaf node determines its code. |
| 95 | +- **Assigning Codes:** Traverse the Huffman tree to assign codes to each character, with shorter codes for more frequent characters. |
| 96 | + |
| 97 | +## Huffman Coding Code in Python |
| 98 | + |
| 99 | +```python |
| 100 | +from heapq import heappush, heappop, heapify |
| 101 | +from collections import defaultdict |
| 102 | + |
| 103 | +def huffman_coding(data): |
| 104 | + frequency = defaultdict(int) |
| 105 | + for char in data: |
| 106 | + frequency[char] += 1 |
| 107 | + |
| 108 | + heap = [[weight, [symbol, ""]] for symbol, weight in frequency.items()] |
| 109 | + heapify(heap) |
| 110 | + |
| 111 | + while len(heap) > 1: |
| 112 | + lo = heappop(heap) |
| 113 | + hi = heappop(heap) |
| 114 | + for pair in lo[1:]: |
| 115 | + pair[1] = '0' + pair[1] |
| 116 | + for pair in hi[1:]: |
| 117 | + pair[1] = '1' + pair[1] |
| 118 | + heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:]) |
| 119 | + |
| 120 | + return sorted(heappop(heap)[1:], key=lambda p: (len(p[-1]), p)) |
| 121 | + |
| 122 | +data = "Huffman coding is a greedy algorithm" |
| 123 | +encoded_data = huffman_coding(data) |
| 124 | +print("Huffman Codes:") |
| 125 | +for symbol, code in encoded_data: |
| 126 | + print(f"{symbol}: {code}") |
| 127 | +``` |
| 128 | + |
| 129 | +## Complexity Analysis |
| 130 | +- **Time Complexity**: O(n log n) for heap operations, where n is the number of unique characters |
| 131 | +- **Space Complexity**: O(n) for the heap |
| 132 | + |
| 133 | +</br> |
| 134 | +<hr> |
| 135 | +</br> |
0 commit comments