ADA-Python / algorithms /greedy.py
ussu321's picture
Upload 50 files
bd86de0 verified
Raw
History Blame Contribute Delete
6.57 kB
"""
USSU Algorithm Analyzer v4.0 - Greedy Algorithms Suite
Activity Selection, Huffman, Fractional Knapsack, and more.
"""
import heapq
from typing import List, Dict, Tuple, Any
from utils.core import profile_algorithm, OperationCounter
class GreedyAlgorithms(OperationCounter):
"""Greedy algorithm collection for ADA"""
def __init__(self):
super().__init__()
def reset(self):
self.reset_counters()
# ==================== ACTIVITY SELECTION ====================
@profile_algorithm
def activity_selection(self, activities: List[Tuple[int, int, str]]) -> Dict:
"""
activities: list of (start, finish, name)
Returns maximum set of non-conflicting activities.
"""
self.reset()
# Sort by finish time
sorted_acts = sorted(activities, key=lambda x: x[1])
selected = [sorted_acts[0]]
last_finish = sorted_acts[0][1]
for i in range(1, len(sorted_acts)):
self.iterations += 1
self.comparisons += 1
if sorted_acts[i][0] >= last_finish:
selected.append(sorted_acts[i])
last_finish = sorted_acts[i][1]
return {
'algorithm': 'Activity Selection (Greedy)',
'selected': selected,
'count': len(selected),
'time_complexity': 'O(n log n)',
'space_complexity': 'O(n)',
'iterations': self.iterations,
'comparisons': self.comparisons,
}
# ==================== FRACTIONAL KNAPSACK ====================
@profile_algorithm
def fractional_knapsack(self, weights: List[int], values: List[int], capacity: int) -> Dict:
self.reset()
n = len(weights)
items = [(values[i] / weights[i], weights[i], values[i], i) for i in range(n)]
items.sort(reverse=True) # Sort by value/weight ratio descending
total_value = 0.0
selected = []
remaining = capacity
for ratio, w, v, idx in items:
self.iterations += 1
self.comparisons += 1
if remaining >= w:
selected.append((idx, 1.0, v))
total_value += v
remaining -= w
else:
fraction = remaining / w
selected.append((idx, fraction, v * fraction))
total_value += v * fraction
remaining = 0
break
return {
'algorithm': 'Fractional Knapsack (Greedy)',
'max_value': total_value,
'selected_items': selected,
'time_complexity': 'O(n log n)',
'space_complexity': 'O(n)',
'iterations': self.iterations,
'comparisons': self.comparisons,
}
# ==================== HUFFMAN CODING ====================
@profile_algorithm
def huffman_coding(self, frequencies: Dict[str, int]) -> Dict:
self.reset()
if len(frequencies) == 0:
return {'algorithm': 'Huffman Coding', 'codes': {}, 'time_complexity': 'O(n log n)', 'space_complexity': 'O(n)'}
heap = [[weight, [symbol, ""]] for symbol, weight in frequencies.items()]
heapq.heapify(heap)
while len(heap) > 1:
self.iterations += 1
lo = heapq.heappop(heap)
hi = heapq.heappop(heap)
for pair in lo[1:]:
pair[1] = '0' + pair[1]
for pair in hi[1:]:
pair[1] = '1' + pair[1]
heapq.heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:])
result = sorted(heapq.heappop(heap)[1:], key=lambda p: (len(p[-1]), p))
codes = {symbol: code for symbol, code in result}
# Calculate total bits
total_bits = sum(frequencies[s] * len(codes[s]) for s in frequencies)
return {
'algorithm': 'Huffman Coding',
'codes': codes,
'total_bits': total_bits,
'average_bits': total_bits / sum(frequencies.values()),
'time_complexity': 'O(n log n)',
'space_complexity': 'O(n)',
'iterations': self.iterations,
}
# ==================== JOB SEQUENCING WITH DEADLINES ====================
@profile_algorithm
def job_sequencing(self, jobs: List[Tuple[str, int, int]]) -> Dict:
"""
jobs: list of (job_id, deadline, profit)
"""
self.reset()
n = len(jobs)
# Sort by profit descending
jobs_sorted = sorted(jobs, key=lambda x: x[2], reverse=True)
max_deadline = max(j[1] for j in jobs)
slot = [-1] * (max_deadline + 1)
scheduled = []
total_profit = 0
for job_id, deadline, profit in jobs_sorted:
self.iterations += 1
# Find latest available slot before deadline
for t in range(min(deadline, max_deadline), 0, -1):
self.comparisons += 1
if slot[t] == -1:
slot[t] = job_id
scheduled.append((job_id, t))
total_profit += profit
break
return {
'algorithm': 'Job Sequencing with Deadlines',
'scheduled_jobs': scheduled,
'total_profit': total_profit,
'time_complexity': 'O(n²)',
'space_complexity': 'O(n)',
'iterations': self.iterations,
'comparisons': self.comparisons,
}
# ==================== MINIMUM COINS (GREEDY) ====================
@profile_algorithm
def minimum_coins_greedy(self, coins: List[int], amount: int) -> Dict:
"""Greedy coin change - works for canonical systems like US coins"""
self.reset()
coins_sorted = sorted(coins, reverse=True)
count = 0
used = []
remaining = amount
for coin in coins_sorted:
self.iterations += 1
if remaining >= coin:
num = remaining // coin
count += num
used.extend([coin] * num)
remaining -= num * coin
optimal = remaining == 0
return {
'algorithm': 'Minimum Coins (Greedy)',
'min_coins': count if optimal else -1,
'coins_used': used if optimal else [],
'optimal': optimal,
'warning': None if optimal else 'Greedy may not be optimal for this coin system!',
'time_complexity': 'O(|coins|)',
'space_complexity': 'O(1)',
'iterations': self.iterations,
}