Repository: keon/algorithms
Pull request #491 (2019-03-29 07:13:25)

fix bugs, add descriptions, and add tests

coveralls: ## Pull Request Test Coverage Report for [Build 880](https://coveralls.io/builds/22472769)

* **59** of **60**   **(98.33%)**  changed or added relevant lines in **4** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**3.03%**) to **72.214%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [algorithms/dp/job_scheduling.py](https://coveralls.io/builds/22472769/source?filename=algorithms%2Fdp%2Fjob_scheduling.py#L57) | 1 | 2 | 50.0%
<!-- | **Total:** | **59** | **60** | **98.33%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/22472769/badge)](https://coveralls.io/builds/22472769) |
| :-- | --: |
| Change from base [Build 879](https://coveralls.io/builds/22453121): |  3.03% |
| Covered Lines: | 5372 |
| Relevant Lines: | 7439 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/algorithms/bit/count_ones.py b/algorithms/bit/count_ones.py
index e96161e..ac718f3 100644
--- a/algorithms/bit/count_ones.py
+++ b/algorithms/bit/count_ones.py
@@ -3,7 +3,7 @@ Write a function that takes an unsigned integer and
 returns the number of '1' bits it has
 (also known as the Hamming weight).
 
-For example, the 32-bit integer '1' has binary
+For example, the 32-bit integer '11' has binary
 representation 00000000000000000000000000001011,
 so the function should return 3.
 
diff --git a/algorithms/dp/__init__.py b/algorithms/dp/__init__.py
index 174f524..8925409 100644
--- a/algorithms/dp/__init__.py
+++ b/algorithms/dp/__init__.py
@@ -1,1 +1,20 @@
+from .buy_sell_stock import *
+from .climbing_stairs import *
+from .coin_change import *
+from .combination_sum import *
 from .edit_distance import *
+from .egg_drop import *
+from .fib import *
+from .hosoya_triangle import *
+from .house_robber import *
+from .job_scheduling import *
+from .knapsack import *
+from .longest_increasing import *
+from .matrix_chain_order import *
+from .max_product_subarray import *
+from .max_subarray import *
+from .min_cost_path import *
+from .num_decodings import *
+from .regex_matching import *
+from .rod_cut import *
+from .word_break import *
diff --git a/algorithms/dp/climbing_stairs.py b/algorithms/dp/climbing_stairs.py
index 6f5f3b6..0c02efe 100644
--- a/algorithms/dp/climbing_stairs.py
+++ b/algorithms/dp/climbing_stairs.py
@@ -17,7 +17,7 @@ def climb_stairs(n):
     :rtype: int
     """
     arr = [1, 1]
-    for i in range(2, n+1):
+    for _ in range(1, n):
         arr.append(arr[-1] + arr[-2])
     return arr[-1]
 
diff --git a/algorithms/dp/coin_change.py b/algorithms/dp/coin_change.py
index dce9bb5..db14796 100644
--- a/algorithms/dp/coin_change.py
+++ b/algorithms/dp/coin_change.py
@@ -2,7 +2,7 @@
 Problem
 Given a value N, if we want to make change for N cents, and we have infinite supply of each of 
 S = { S1, S2, .. , Sm} valued //coins, how many ways can we make the change? 
-The order of coins doesn’t matter.
+The order of coins doesn't matter.
 For example, for N = 4 and S = [1, 2, 3], there are four solutions: 
 [1, 1, 1, 1], [1, 1, 2], [2, 2], [1, 3]. 
 So output should be 4. 
@@ -35,14 +35,3 @@ def count(s, n):
             table[i][j] = x + y
  
     return table[n][m-1]
-
-
-if __name__ == '__main__':
-    
-    coins = [1, 2, 3]
-    n = 4
-    assert count(coins, n) == 4
-    
-    coins = [2, 5, 3, 6]
-    n = 10
-    assert count(coins, n) == 5
diff --git a/algorithms/dp/combination_sum.py b/algorithms/dp/combination_sum.py
index 67bc6af..57587ff 100644
--- a/algorithms/dp/combination_sum.py
+++ b/algorithms/dp/combination_sum.py
@@ -58,9 +58,3 @@ def combination_sum_bottom_up(nums, target):
             if i - nums[j] >= 0:
                 comb[i] += comb[i - nums[j]]
     return comb[target]
-
-
-combination_sum_topdown([1, 2, 3], 4)
-print(dp[4])
-
-print(combination_sum_bottom_up([1, 2, 3], 4))
diff --git a/algorithms/dp/egg_drop.py b/algorithms/dp/egg_drop.py
index c087982..d07bee8 100644
--- a/algorithms/dp/egg_drop.py
+++ b/algorithms/dp/egg_drop.py
@@ -1,14 +1,35 @@
+"""
+You are given K eggs, and you have access to a building with N floors 
+from 1 to N. Each egg is identical in function, and if an egg breaks, 
+you cannot drop it again. You know that there exists a floor F with 
+0 <= F <= N such that any egg dropped at a floor higher than F will 
+break, and any egg dropped at or below floor F will not break. 
+Each move, you may take an egg (if you have an unbroken one) and drop 
+it from any floor X (with 1 <= X <= N). Your goal is to know with 
+certainty what the value of F is. What is the minimum number of moves 
+that you need to know with certainty what F is, regardless of the 
+initial value of F?
+
+Example:
+Input: K = 1, N = 2
+Output: 2
+Explanation: 
+Drop the egg from floor 1.  If it breaks, we know with certainty that F = 0.
+Otherwise, drop the egg from floor 2.  If it breaks, we know with 
+certainty that F = 1.
+If it didn't break, then we know with certainty F = 2.
+Hence, we needed 2 moves in the worst case to know what F is with certainty.
+"""
+
 # A Dynamic Programming based Python Program for the Egg Dropping Puzzle
 INT_MAX = 32767
- 
-# Function to get minimum number of trials needed in worst
-# case with n eggs and k floors
+
 def egg_drop(n, k):
     # A 2D table where entery eggFloor[i][j] will represent minimum
     # number of trials needed for i eggs and j floors.
     egg_floor = [[0 for x in range(k+1)] for x in range(n+1)]
  
-    # We need one trial for one floor and0 trials for 0 floors
+    # We need one trial for one floor and 0 trials for 0 floors
     for i in range(1, n+1):
         egg_floor[i][1] = 1
         egg_floor[i][0] = 0
diff --git a/algorithms/dp/fib.py b/algorithms/dp/fib.py
index 765ee2f..6762e90 100644
--- a/algorithms/dp/fib.py
+++ b/algorithms/dp/fib.py
@@ -63,11 +63,10 @@ def fib_iter(n):
     sum = 0
     if n <= 1:
         return n
-    for i in range(n-1):
+    for _ in range(n-1):
         sum = fib_1 + fib_2
         fib_1 = fib_2
         fib_2 = sum
     return sum
 
-# => 354224848179261915075
-# print(fib_iter(100))
+# print(fib_iter(100)) # => 354224848179261915075
diff --git a/algorithms/dp/house_robber.py b/algorithms/dp/house_robber.py
index 88baaa9..a2a8a6d 100644
--- a/algorithms/dp/house_robber.py
+++ b/algorithms/dp/house_robber.py
@@ -19,7 +19,3 @@ def house_robber(houses):
         now = max(last + house, now)
         last = tmp
     return now
-
-houses = [1, 2, 16, 3, 15, 3, 12, 1]
-
-print(house_robber(houses))
diff --git a/algorithms/dp/job_scheduling.py b/algorithms/dp/job_scheduling.py
index 7c72368..eaf98ec 100644
--- a/algorithms/dp/job_scheduling.py
+++ b/algorithms/dp/job_scheduling.py
@@ -45,7 +45,7 @@ def schedule(job):
     n = len(job) 
     table = [0 for _ in range(n)]
  
-    table[0] = job[0].profit;
+    table[0] = job[0].profit
  
     # Fill entries in table[] using recursive property
     for i in range(1, n):
@@ -54,7 +54,7 @@ def schedule(job):
         incl_prof = job[i].profit
         l = binary_search(job, i)
         if (l != -1):
-            incl_prof += table[l];
+            incl_prof += table[l]
  
         # Store maximum of including and excluding
         table[i] = max(incl_prof, table[i - 1])
diff --git a/algorithms/dp/knapsack.py b/algorithms/dp/knapsack.py
index e906ae0..65f7d89 100644
--- a/algorithms/dp/knapsack.py
+++ b/algorithms/dp/knapsack.py
@@ -30,9 +30,3 @@ def get_maximum_value(items, capacity):
                                            dp[current_weight] + item.value)
         dp = dp_tmp
     return max(dp)
-
-
-print(get_maximum_value([Item(60, 10), Item(100, 20), Item(120, 30)],
-                        50))
-print(get_maximum_value([Item(60, 5), Item(50, 3), Item(70, 4), Item(30, 2)],
-                        5))
diff --git a/algorithms/dp/longest_increasing.py b/algorithms/dp/longest_increasing.py
index 5f6eb83..7197137 100644
--- a/algorithms/dp/longest_increasing.py
+++ b/algorithms/dp/longest_increasing.py
@@ -1,4 +1,14 @@
+"""
+Given an unsorted array of integers, find the length of longest increasing subsequence.
 
+Example:
+
+Input: [10,9,2,5,3,7,101,18]
+Output: 4 
+Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
+
+The time complexity is O(n^2).
+"""
 
 def longest_increasing_subsequence(sequence):
     """
@@ -14,10 +24,3 @@ def longest_increasing_subsequence(sequence):
                 counts[i] = max(counts[i], counts[j] + 1)
                 print(counts)
     return max(counts)
-
-
-sequence = [1, 101, 10, 2, 3, 100, 4, 6, 2]
-print("sequence: ", sequence)
-print("output: ", longest_increasing_subsequence(sequence))
-print("answer: ", 5)
-


Test patch:
diff --git a/tests/test_dp.py b/tests/test_dp.py
index 2edaadf..08f6bd0 100644
--- a/tests/test_dp.py
+++ b/tests/test_dp.py
@@ -1,17 +1,83 @@
 from algorithms.dp import (
+    max_profit_naive, max_profit_optimized,
+    climb_stairs, climb_stairs_optimized,
+    count,
+    combination_sum_topdown, combination_sum_bottom_up,
     edit_distance,
-    hosoya_triangle
+    egg_drop,
+    fib_recursive, fib_list, fib_iter,
+    hosoya_testing,
+    house_robber,
+    Job, schedule,
+    Item, get_maximum_value,
+    longest_increasing_subsequence
 )
 
 
 import unittest
 
 
+class TestBuySellStock(unittest.TestCase):
+    def test_max_profit_naive(self):
+        self.assertEqual(max_profit_naive([7, 1, 5, 3, 6, 4]), 5)
+        self.assertEqual(max_profit_naive([7, 6, 4, 3, 1]), 0)
+
+    def test_max_profit_optimized(self):
+        self.assertEqual(max_profit_optimized([7, 1, 5, 3, 6, 4]), 5)
+        self.assertEqual(max_profit_optimized([7, 6, 4, 3, 1]), 0)
+
+
+class TestClimbingStairs(unittest.TestCase):
+    def test_climb_stairs(self):
+        self.assertEqual(climb_stairs(2), 2)
+        self.assertEqual(climb_stairs(10), 89)
+    
+    def test_climb_stairs_optimized(self):
+        self.assertEqual(climb_stairs_optimized(2), 2)
+        self.assertEqual(climb_stairs_optimized(10), 89)
+
+
+class TestCoinChange(unittest.TestCase):
+    def test_count(self):
+        self.assertEqual(count([1, 2, 3], 4), 4)
+        self.assertEqual(count([2, 5, 3, 6], 10), 5)
+
+
+class TestCombinationSum(unittest.TestCase):
+    def test_combination_sum_topdown(self):
+        self.assertEqual(combination_sum_topdown([1, 2, 3], 4), 7)
+    
+    def test_combination_sum_bottom_up(self):
+        self.assertEqual(combination_sum_bottom_up([1, 2, 3], 4), 7)
+
+
 class TestEditDistance(unittest.TestCase):
     def test_edit_distance(self):
         self.assertEqual(edit_distance('food', 'money'), 4)
         self.assertEqual(edit_distance('horse', 'ros'), 3)
 
+
+class TestEggDrop(unittest.TestCase):
+    def test_egg_drop(self):
+        self.assertEqual(egg_drop(1, 2), 2)
+        self.assertEqual(egg_drop(2, 6), 3)
+        self.assertEqual(egg_drop(3, 14), 4)
+
+
+class TestFib(unittest.TestCase):
+    def test_fib_recursive(self):
+        self.assertEqual(fib_recursive(10), 55)
+        self.assertEqual(fib_recursive(30), 832040)
+    
+    def test_fib_list(self):
+        self.assertEqual(fib_list(10), 55)
+        self.assertEqual(fib_list(30), 832040)
+    
+    def test_fib_iter(self):
+        self.assertEqual(fib_iter(10), 55)
+        self.assertEqual(fib_iter(30), 832040)
+
+
 class TestHosoyaTriangle(unittest.TestCase):
     """[summary]
     Test for the file hosoya_triangle
@@ -21,14 +87,14 @@ class TestHosoyaTriangle(unittest.TestCase):
     """
     
     def test_hosoya(self):
-        self.assertEqual([1], hosoya_triangle.hosoya_testing(1))
+        self.assertEqual([1], hosoya_testing(1))
         self.assertEqual([1,
                          1, 1,
                          2, 1, 2,
                          3, 2, 2, 3,
                          5, 3, 4, 3, 5,
                          8, 5, 6, 6, 5, 8],
-                         hosoya_triangle.hosoya_testing(6))
+                         hosoya_testing(6))
         self.assertEqual([1,
                           1, 1,
                           2, 1, 2,
@@ -39,7 +105,33 @@ class TestHosoyaTriangle(unittest.TestCase):
                           21, 13, 16, 15, 15, 16, 13, 21,
                           34, 21, 26, 24, 25, 24, 26, 21, 34,
                           55, 34, 42, 39, 40, 40, 39, 42, 34, 55],
-                          hosoya_triangle.hosoya_testing(10))
+                          hosoya_testing(10))
+
+
+class TestHouseRobber(unittest.TestCase):    
+    def test_house_robber(self):
+        self.assertEqual(44, house_robber([1, 2, 16, 3, 15, 3, 12, 1]))
+
+
+class TestJobScheduling(unittest.TestCase):
+    def test_job_scheduling(self):
+        job1, job2 = Job(1, 3, 2), Job(2, 3, 4)
+        self.assertEqual(4, schedule([job1, job2]))
+
+
+class TestKnapsack(unittest.TestCase):
+    def test_get_maximum_value(self):
+        item1, item2, item3 = Item(60, 10), Item(100, 20), Item(120, 30)
+        self.assertEqual(220, get_maximum_value([item1, item2, item3], 50))
+
+        item1, item2, item3, item4 = Item(60, 5), Item(50, 3), Item(70, 4), Item(30, 2)
+        self.assertEqual(80, get_maximum_value([item1, item2, item3, item4], 5))
+
+
+class TestLongestIncreasingSubsequence(unittest.TestCase):
+    def test_longest_increasing_subsequence(self):
+        sequence = [1, 101, 10, 2, 3, 100, 4, 6, 2]
+        self.assertEqual(5, longest_increasing_subsequence(sequence))
 
 
 if __name__ == '__main__':