Skip to content

Commit

Permalink
[chore] add markdown versions of DP notebooks
Browse files Browse the repository at this point in the history
  • Loading branch information
gitgik committed Feb 25, 2021
1 parent 2d4e155 commit 66f332c
Show file tree
Hide file tree
Showing 13 changed files with 904 additions and 0 deletions.
76 changes: 76 additions & 0 deletions dynamic-programming/disk_stacking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
### Disk stacking
An array's element represents a disk. Each element has `[length, width, height]`
Write a function that will stack the disks and build a tower with the greatest height, such that each disk has strictly lower dimensions than those below it.

Sample input
```
array = [[2, 1, 2], [3, 2, 3], [2, 2, 8], [2, 3, 4], [1, 3, 1], [4, 4, 5]]
```

Sample output:
```
[[4, 4, 5], [3, 2, 3], [2, 1, 2]]
The tower looks something like this
[2, 1, 2]
[ 3, 2, 3 ]
[4, 4, 5]
```



```python
def diskStacking(disks):
"""O(n) space, O(n^2) time"""
# sort based on height
disks.sort(key=lambda disk: disk[2])
diskHeights = [disk[2] for disk in disks]
sequences = [None for disk in disks]

# maxHeightIndex
maxHeightIndex = 0

for i in range(1, len(disks)):
currentDisk = disks[i]
for j in range(0, i):
otherDisk = disks[j]
# validate dimensions
if areValidDimensions(otherDisk, currentDisk):
if diskHeights[i] <= currentDisk[2] + diskHeights[j]:
diskHeights[i] = currentDisk[2] + diskHeights[j]
sequences[i] = j
# update maxHeightIndex: which will help us backtrack to find the disks involved.
if diskHeights[i] >= diskHeights[maxHeightIndex]:
maxHeightIndex = i
return buildSequence(disks, sequences, maxHeightIndex)

def buildSequence(array, sequences, currentIdx):
results = []
while currentIdx is not None:
results.append(disks[currentIdx])
currentIdx = sequences[currentIdx]
return results

def areValidDimensions(o, c):
return o[0] < c[0] and o[1] < c[1] and o[2] < c[2]

```


```python
disks = [[2, 1, 2], [3, 2, 3], [2, 2, 8], [2, 3, 4], [1, 3, 1], [4, 4, 5]]
diskStacking(disks)
```




[[4, 4, 5], [3, 2, 3], [2, 1, 2]]




```python

```
46 changes: 46 additions & 0 deletions dynamic-programming/fibonacci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
```python
def fibonacci(n):
dp = [0, 1]
for i in range(2, n + 1):
dp.append(dp[i - 1] + dp[i - 2])
return dp[n]
```


```python
def fibo(n, memoize={1:0,2:1}):
if n < 2:
return n

memoize[n] = fibo(n - 1, memoize) + fibo(n - 2, memoize)
return memoize[n]
```


```python
fibo(5)
```




5




```python
fibonacci(6)
```




8




```python

```
56 changes: 56 additions & 0 deletions dynamic-programming/find_denominations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
### Problem
You are given an array of length N, where each element i represents the number of ways we can produce i units of change. For example, [1, 0, 1, 1, 2] shows that there is only one way to make [0, 2, or 3] units, and 2 ways of making 4 units.

Given such an array, determine the denominations that must be in use. In the case above, there must be coins with value 2, 3, and 4.

### Approach

There are two situation that will make the value at that index i be incremented:
1. Coin `i` is one of the denominations
2. Another coin `j` is one of the denominations and the value of array[i - j] is also non-zero

For each index:
- Check the lower denominations j that are known to be part of the solution.
- Each time we find `i - j` to be non-zero, we have encountered one of the ways of getting to element `i`, so we decrement the value at that index by 1.
- If after going through all coins and the value at that index is still positive, we know that we must include `i` as part of the solution

We must take note not to double count. For example, when `i == 7` and [3, 4] are in our solution set, that is one way of producing 7.
When two coins sum up to an index, only the lower one cause us to decrement the value at that index.




```python
def find_denominations(array):
coins = set()
# start from index 1, and start counting from 1 (not zero-based index)
for i, val in enumerate(array[1:], 1):
if val > 0:
for j in coins:
if array[i - j] > 0 and (i - j not in coins or i - j <= j):
val -= 1
if val > 0:
coins.add(i)
return coins
```


```python
find_denominations([1, 0, 1, 1, 2])
```




{2, 3, 4}



The time complexity of this algorithm is O(M * N), where M is the number of coins in our solution and N is the length of our input.

The space complexity will be O(M), since we require extra space to store the set of solution coins.


```python

```
66 changes: 66 additions & 0 deletions dynamic-programming/kadanes_algorithm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
### Problem
Given an array of integers, find the largest possible sum of contiguous subarray within the given array.


```python
## solution
def kadanes_algorithm(array):
"""Complexity: O(n) time | O(1) space"""

local_max = global_max = array[0]
for i in range(1, len(array)):
# the local maximum for an index is always => max(array[i], local_max of array[i - 1])
local_max = max(array[i], array[i] + local_max)
global_max = max(global_max, local_max)
return global_max
```


```python
array = [-3, 4, -1, 2, 1, -5, 4, -12, 3, -7]
kadanes_algorithm(array)
```




6




```python
## unit tests to validate solution
import unittest

class KadenesTestCase(unittest.TestCase):
def test_case_1(self):
array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
expected_sum = 55
self.assertEqual(kadanes_algorithm(array), expected_sum)

def test_case_2(self):
array = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
expected_sum = 6
self.assertEqual(kadanes_algorithm(array), expected_sum)

if __name__ == "__main__":
unittest.main(argv=['first-arg-is-ignored'], exit=False)
```


```python
kadanes_algorithm([1, -1, 2, 3, -6])
```




5




```python

```
92 changes: 92 additions & 0 deletions dynamic-programming/knapsack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
## Knapsack Problem
You are given an array of arrays. Each subarray in this array contains two integer elements: the first represents an item value, the second an item's weight.

You are also given an integer representing the maximum capacity of the knapsack that you have.

Your goal is to fit items that will collectively fit within the knapsack's capacity while maximizing on their combined value.

Write a function that returns the maximum combined value of the items you'd pick, as well as an array of the item indices picked.

Sample Input: [[1,2],[4,3],[5,6],[6,7]], 10
Sample Output: 10, [1, 3]




```python
def knapsack(items, capacity):
"""O(nc) time | O(nc) space, where n == number of items, c == capacity"""
knapsack_values = [[0 for col in range(capacity + 1)] for row in range(len(items) + 1)]

for row in range(1, len(items) + 1):
value = items[row - 1][0]
weight = items[row - 1][1]
for col in range(capacity + 1):
if weight > col: # col here represents a given capacity
knapsack_values[row][col] = knapsack_values[row - 1][col]
else:
knapsack_values[row][col] = max(
knapsack_values[row - 1][col],
knapsack_values[row - 1][col - weight] + value
)

return [knapsack_values[-1][-1], backtrack_items(knapsack_values, items)]

def backtrack_items(values, items):
"""
Backtracks from the bottom right of the 2d array, finding all the items that were put in the knapsack.
"""
results = []
row = len(values) - 1
col = len(values[0]) - 1

while row > 0:
if values[row][col] == values[row - 1][col]:
row -= 1
else:
results.append(row - 1)
col -= items[row - 1][1]
row -= 1
return list(reversed(results))
```


```python
# test it out
capacity = 10
items = [[1, 3], [4, 3], [5, 6], [6, 7]]

knapsack(items, capacity)
```




[10, [1, 3]]



### Unit Tests


```python
# unit tests
import unittest

class KnapsackTestCase(unittest.TestCase):
def test_case_1(self):
capacity = 10
items = [[1, 3], [4, 3], [5, 6], [6, 7]]
expected = [10, [1, 3]]
self.assertEqual(knapsack(items, capacity), expected)

def test_case_when_capacity_is_zero(self):
capacity = 0
items = [[1, 3], [4, 3], [5, 6], [6, 7]]
expected = [0, []]
self.assertEqual(knapsack(items, capacity), expected)


if __name__ == "__main__":
unittest.main(argv=[""], exit=False)
```
Loading

0 comments on commit 66f332c

Please sign in to comment.