-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20 from ghimiresdp/feature/problem-solving
add Problem Solving
- Loading branch information
Showing
7 changed files
with
395 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Problem Solving | ||
|
||
This section will help us become more proficient with problem solving skills | ||
with Python Programming Language. | ||
|
||
This section primarily contains 2 different sections which are as follows: | ||
|
||
## Basic Problem Solving | ||
|
||
This section helps us warming up with basic problem solving skills. It's goal is | ||
to review our previously learned python notes section. | ||
|
||
Some of the Basic Problem Solving solutions are as follows: | ||
|
||
1. [Practical Number Solution](basic/practical_number.py) | ||
2. Iteration with Comprehension | ||
3. [Greatest Common Divisor](basic/gcd.py) | ||
4. Matrix Multiplication | ||
5. Median Calculation | ||
6. [Reverse digits of an integer](basic/reverse_digits.py) | ||
|
||
|
||
## Dynamic Programming | ||
|
||
This section helps us solving advanced problems more efficiently and using more | ||
optimum solutions. Solving this type of problem helps us efficiently solve | ||
problems while managing time and space complexity efficiently. | ||
|
||
Some of the Dynamic Programming solutions are as follows: | ||
|
||
1. [Coin Change Problem](dp/coin_change.py) | ||
2. [Fibonacci Series Problem](dp/fibonacci.py) | ||
3. Palindrome Partition Problem | ||
4. Minimizing the sum of list of integers | ||
5. Longest Common Subsequence Problem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
""" | ||
Greatest Common Divisor (GCD) | ||
----------------------------- | ||
Greatest Common Divisor or the highest common factor is a common divisor of the | ||
given numbers. | ||
It is the largest positive integer that divides both numbers without leaving | ||
remainders. | ||
For example numbers 12 and 18 has GCD 6 | ||
""" | ||
|
||
from functools import reduce | ||
|
||
|
||
def _gcd(a: int, b: int): | ||
""" | ||
Finding out GCD between 2 numbers is so easy in python. we try to perform | ||
modulo operator between 2 numbers until we find remainder to be 0. | ||
For example we have 2 numbers 48 and 64 | ||
a = 48, b = 64 | ||
Step 1: (a, b) = (b, a%b) = (64, 48 % 64) = (64, 48) b != 0 | ||
Step 2: (a, b) = (48, 64 % 48) = (48, 16) b != 0 | ||
Step 3: (a, b) = (16, 48 % 16) = (16, 0) b == 0 | ||
Setp 4: b == 0, so GCD = a = 16 | ||
""" | ||
# find out gcd between 2 numbers | ||
while b: | ||
a, b = b, a % b | ||
return a | ||
|
||
|
||
def gcd(numbers: list[int]) -> int: | ||
""" | ||
To find out gcd between multiple numbers, we simply reduce the above _gcd | ||
function to each numbers in the list. | ||
""" | ||
return reduce(_gcd, numbers) | ||
|
||
|
||
if __name__ == "__main__": | ||
print(_gcd(1, 2)) # 1 | ||
print(_gcd(12, 18)) # 6 | ||
print(_gcd(9, 18)) # 9 | ||
print(_gcd(32, 40)) # 8 | ||
|
||
print(gcd([1, 2])) # 1 | ||
print(gcd([32, 40, 80])) # 8 | ||
print(gcd([32, 64, 96])) # 32 | ||
print(gcd([64, 96, 160, 320])) # 32 | ||
print(gcd([64, 96, 160, 320, 48])) # 16 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
""" | ||
# Practical Number Solution | ||
--------------------------- | ||
A practical number is a positive integer where every smaller can be expressed as | ||
a sum of distinct divisors of that number. | ||
In other words, a number is a practical number whose divisors can be combined to | ||
create a number below it. | ||
For example 12 is a practical number. | ||
It's divisors are: 1, 2, 3, 4, and 6. | ||
1 -> 1 | ||
2 -> 2 | ||
3 -> 3 | ||
4 -> 4 | ||
5 -> 4 + 1 | ||
6 -> 6 | ||
7 -> 7 + 1 | ||
8 -> 6 + 2 | ||
9 -> 6 + 3 | ||
10 -> 6 + 4 | ||
11 -> 6 + 4 + 1 | ||
""" | ||
|
||
import math | ||
|
||
|
||
def find_divisors(num: int) -> list[int]: | ||
divisors = {1} | ||
for n in range(2, int(math.sqrt(num)) + 1): | ||
if num % n == 0: | ||
divisors.add(n) | ||
divisors.add(num // n) | ||
return list(divisors) | ||
|
||
|
||
def has_sum(target: int, _set: list[int]): | ||
# if the number itself exists, or number return True | ||
if target == 0 or target in _set: | ||
return True | ||
|
||
sums = {0} | ||
|
||
for num in _set: | ||
new_sums = {x + num for x in sums} | ||
sums.update(new_sums) | ||
if target in sums: | ||
return True | ||
return False | ||
|
||
|
||
def is_practical_number(num: int) -> bool: | ||
if num < 1: | ||
return False | ||
divisors = find_divisors(num) | ||
sum_previous = 1 | ||
|
||
for i in range(1, len(divisors)): | ||
if divisors[i] > sum_previous + 1: | ||
return False | ||
sum_previous += divisors[i] | ||
|
||
for n in range(1, num): | ||
if not has_sum(n, divisors): | ||
return False | ||
return True | ||
|
||
|
||
if __name__ == "__main__": | ||
for number in [8, 10, 12, 15]: | ||
print( | ||
f"{number:<5d}: {'✅' if is_practical_number(number) else '⛔ Not'} Practical" | ||
) | ||
|
||
""" | ||
OUTPUT | ||
--------- | ||
8 : ✅ Practical | ||
10 : ⛔ Not Practical | ||
12 : ✅ Practical | ||
15 : ⛔ Not Practical | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
""" | ||
Reverse Digits of an integer | ||
---------------------------- | ||
Given an integer, reverse all the digits of an integer mathematically. Example, | ||
if an integer 12345 is given, the reversed integer should be 54321. | ||
Mathematically reversing an integer requires a loop that divides the integer and | ||
find outs the remainder to know the digit of one place and multiplying back by | ||
10 and adding the remainder of the previous iteration until the number becomes 0. | ||
Steps: | ||
1. result = 0 | ||
2. divide the integer by 10 and find out remainder and result | ||
- number = num // 10 -> 12345 // 10 = 1234 | ||
- r = num % 10 -> 12345 %10 = 5 | ||
- num = number -> 1234 | ||
3. now multiply the previous remainder by 10 and add with the current remainder | ||
- result = result * 10 + r | ||
4. Repeat steps 2 and 3 until number becomes 0 | ||
""" | ||
|
||
|
||
def reverse_digits(number: int) -> int: | ||
result = 0 | ||
if number < 0: | ||
raise ValueError("Negative numbers not allowed") | ||
if number < 10: | ||
return number | ||
while number > 0: | ||
number, result = (number // 10, result * 10 + number % 10) | ||
return result | ||
|
||
|
||
if __name__ == "__main__": | ||
print(reverse_digits(123)) # 321 | ||
print(reverse_digits(12345)) # 54321 | ||
print(reverse_digits(87214)) # 41278 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
""" | ||
Coin Change Problem | ||
------------------- | ||
A coin change problem requires us to find out the minimum number of coins from | ||
the given set of coins to change the given amount considering that any number of | ||
coins can be used from the given coin options. | ||
The function should return the minimum number of coins that can be used to | ||
change the given sum. If there are no changes, the function should return -1. | ||
Example we have coins: 1, 2, 5 and we have to change coins with amount 11. | ||
We may have different options: | ||
1. 1 X 11 coins = 11 coins | ||
2. 2 X 5 coins + 1 X 1 coins = 6 coins | ||
3. 5 X 2 coins + 1 X 1 coins = 3 coins | ||
4 1 X 6 coins + 5 X 1 coins = 7 coins | ||
.... | ||
and so on | ||
but the change with option 3 will have minimum number of coins to change. so the | ||
solution will be `3`. | ||
We solve it using dynamic programming | ||
intermediate sums: | ||
--------------------+----------------------------------------------------------- | ||
n -> changes | Remarks | ||
--------------------+----------------------------------------------------------- | ||
0 -> 0 | | ||
1 -> 1 | [1 coin of 1] | ||
2 -> 1 | [1 coin of 2] | ||
3 -> 2 | [ 1 + { table(n-2)) = 1 }] = 2 | ||
4 -> 2 | [ 1 + { table(n-2) = 1 }] = 2 | ||
5 -> 1 | [1 coin of 5] | ||
6 -> 2 | [1 +{ table(n-5) = 1}] = 2 | ||
7 -> 2 | [1 +{ table(n-5) = 1}] = 2 | ||
8 -> 3 | [1 +{ table(n-5) = 2}] = 3 | ||
9 -> 3 | [1 +{ table(n-5) = 2}] = 3 | ||
10 -> 2 | [1 +{ table(n-5) = 1}] = 2 | ||
11 -> 3 | [1 +{ table(n-5) = 2}] = 3 | ||
--------------------+----------------------------------------------------------- | ||
for sum 11, we have largest coin 5 so, we need to find num of changes for | ||
sum of (11-5) = 6, similarly for 6, (6-5) = 1 | ||
number of change for 6 = (number of changes for 1) + 1 | ||
= 1 + 1 | ||
= 2 | ||
number of change for 11 = (number of changes for 6) + 1 | ||
= 2 + 1 | ||
= 3 | ||
""" | ||
|
||
import sys | ||
|
||
|
||
def coin_change(coins: list[int], sum: int) -> int: | ||
if sum == 0: | ||
return 0 | ||
if coins.__len__() == 0: | ||
return -1 | ||
|
||
# initialize a list that stores max number of coins used to change the given | ||
# number from amount 1 to the given sum. | ||
table = [sys.maxsize for _ in range(sum + 1)] | ||
|
||
# we always need 0 number of coins to change the sum 0 from a given options | ||
table[0] = 0 | ||
|
||
# iterate from 1 upto the sum to find number of changes for each amount | ||
# we call it an intermediate sum, which will later be used to find the sum | ||
# for the next number | ||
for intermediate_sum in range(1, sum + 1): | ||
# iterate on each coins to find number of changes required | ||
for coin in coins: # example: [1, 2, 5] | ||
# if the intermediate sum is less than the coin, discard it | ||
# since it cannot be changed with that coin | ||
if intermediate_sum >= coin: | ||
# We find the number of coins to change the amount `num` by | ||
# adding 1 to the previous change that was `num - coin`. | ||
# example: if we were about to change $50, and we already know that | ||
# we have 5 changes for $45 and we have $5 coin for change, we do not need to | ||
# find it from the start. | ||
coins_for_prev_change = table[intermediate_sum - coin] | ||
|
||
# check if the sub result is smallest when iterated over all the | ||
# available coins. | ||
# example: if sub_result when using coin 8 is smaller than using the coin 5, | ||
# we should choose the sub-result with coin 8 since it will give us smallest | ||
# number of coins to change the specified amount. | ||
if ( | ||
coins_for_prev_change != sys.maxsize | ||
and coins_for_prev_change + 1 < table[intermediate_sum] | ||
): | ||
table[intermediate_sum] = coins_for_prev_change + 1 | ||
# print(table) | ||
if table[sum] == sys.maxsize: | ||
return -1 | ||
|
||
return table[sum] | ||
|
||
|
||
if __name__ == "__main__": | ||
print(coin_change([1, 2, 5], 11)) # 3 -> 5,5,1 | ||
print(coin_change([1, 2, 5, 10], 49)) # 7 -> 10X4,5,2,2 | ||
print(coin_change([1, 2, 5, 10], 88)) # 11 -> 10X8,5,2,1 |
Oops, something went wrong.