From f69da2d22e15de5ad70a091e6a747c8a691ab565 Mon Sep 17 00:00:00 2001 From: stonemary Date: Sun, 11 Sep 2016 17:49:46 -0700 Subject: [PATCH 1/3] leetcode math problems --- leetcode/math/__init__.py | 0 leetcode/math/lc263_ugly_number.py | 28 +++++ .../math/lc273_integer_to_english_words.py | 118 ++++++++++++++++++ .../{367.py => math/lc367_perfect_square.py} | 0 leetcode/math/test_lc263_is_ugly_number.py | 38 ++++++ .../test_lc273_integer_to_english_words.py | 38 ++++++ 6 files changed, 222 insertions(+) create mode 100644 leetcode/math/__init__.py create mode 100644 leetcode/math/lc263_ugly_number.py create mode 100644 leetcode/math/lc273_integer_to_english_words.py rename leetcode/{367.py => math/lc367_perfect_square.py} (100%) create mode 100644 leetcode/math/test_lc263_is_ugly_number.py create mode 100644 leetcode/math/test_lc273_integer_to_english_words.py diff --git a/leetcode/math/__init__.py b/leetcode/math/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/leetcode/math/lc263_ugly_number.py b/leetcode/math/lc263_ugly_number.py new file mode 100644 index 0000000..9451cb5 --- /dev/null +++ b/leetcode/math/lc263_ugly_number.py @@ -0,0 +1,28 @@ +""" +Write a program to check whether a given number is an ugly number. + +Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. For example, 6, 8 are ugly while 14 is not +ugly since it includes another prime factor 7. + +Note that 1 is typically treated as an ugly number. +""" + + +def is_ugly_number(number): + if number <= 0: + return False + if number == 1: + return True + + primes = {2, 3, 5} + # keep dividing until it reaches 1 or some other number + + while dividable_by_primes(number, primes): + for prime in primes: + if number % prime == 0: + number /= prime + return number == 1 + + +def dividable_by_primes(number, primes): + return any(number % prime == 0 for prime in primes) diff --git a/leetcode/math/lc273_integer_to_english_words.py b/leetcode/math/lc273_integer_to_english_words.py new file mode 100644 index 0000000..e5a1f61 --- /dev/null +++ b/leetcode/math/lc273_integer_to_english_words.py @@ -0,0 +1,118 @@ +""" +Convert a non-negative integer to its english words representation. +Given input is guaranteed to be less than 2^31 - 1. + +For example, +123 -> "One Hundred Twenty Three" +12345 -> "Twelve Thousand Three Hundred Forty Five" +1234567 -> "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven" + +""" + +import math + + +class Solution(object): + + THOUSAND = 1000 + HUNDRED = 100 + + DIVISION_TO_SUFFIX = { + 0: '', + 1: 'Thousand', + 2: 'Million', + 3: 'Billion' + } + + INT_TO_ENGLISH = { + 1: 'One', + 2: 'Two', + 3: 'Three', + 4: 'Four', + 5: 'Five', + 6: 'Six', + 7: 'Seven', + 8: 'Eight', + 9: 'Nine', + 10: 'Ten', + 11: 'Eleven', + 12: 'Twelve', + 13: 'Thirteen', + 14: 'Fourteen', + 15: 'Fifteen', + 16: 'Sixteen', + 17: 'Seventeen', + 18: 'Eighteen', + 19: 'Nineteen', + 20: 'Twenty', + 30: 'Thirty', + 40: 'Forty', + 50: 'Fifty', + 60: 'Sixty', + 70: 'Seventy', + 80: 'Eighty', + 90: 'Ninety' + } + + def number_to_words(self, num): + """ + :type num: int + :rtype: str + """ + + if num < 0 or num > 2**31 - 1: + raise ValueError('Invalid input: num must be non-negative and less than 2^31 - 1') + + if num == 0: + return 'Zero' + + # fast track + try: + return self.INT_TO_ENGLISH[num] + except KeyError: + pass + + # keep dividing... + division_count = 0 + words = [] + + while num > 0: + # change / to // + num, residual = num // self.THOUSAND, num % self.THOUSAND + if residual: + words.insert(0, self._helper(residual, suffix=self.DIVISION_TO_SUFFIX[division_count])) + division_count += 1 + return ' '.join(words) + + def _helper(self, num, suffix=''): + """num is less than 1000""" + + if num == 0: + return '' + + words = [] + + # get hundreds + hundreds = num // self.HUNDRED + if hundreds: + words.append(self.INT_TO_ENGLISH[hundreds]) + words.append('Hundred') + + # get last two digits + last_two = num % self.HUNDRED + if last_two: + # fast track if possible + try: + words.append(self.INT_TO_ENGLISH[last_two]) + except KeyError: + ones = num % 10 + tens = (num % self.HUNDRED) - ones + if tens: + words.append(self.INT_TO_ENGLISH[tens]) + if ones: + words.append(self.INT_TO_ENGLISH[ones]) + + # apply suffix + if suffix: + words.append(suffix) + return ' '.join(words) diff --git a/leetcode/367.py b/leetcode/math/lc367_perfect_square.py similarity index 100% rename from leetcode/367.py rename to leetcode/math/lc367_perfect_square.py diff --git a/leetcode/math/test_lc263_is_ugly_number.py b/leetcode/math/test_lc263_is_ugly_number.py new file mode 100644 index 0000000..2c60619 --- /dev/null +++ b/leetcode/math/test_lc263_is_ugly_number.py @@ -0,0 +1,38 @@ +import pytest +import random + +from leetcode.math.lc263_ugly_number import is_ugly_number + + +class TestIsUglyNumber(object): + + PRIMES = [2, 3, 5] + + @pytest.mark.parametrize('test_input', [ + 0, + -1234 + ]) + def test_non_positive(self, test_input): + assert not is_ugly_number(test_input) + + def test_one(self): + assert is_ugly_number(1) + + @pytest.mark.parametrize("test_input", PRIMES) + def test_primes(self, test_input): + assert is_ugly_number(test_input) + + def test_multiple_of_primes(self): + turns = random.randint(0, 20) + number = 1 + for i in range(turns): + number *= random.choice(self.PRIMES) + assert is_ugly_number(number) + + def test_ugly_factor(self): + the_uglies = [7, 11, 13, 17, 29] + turns = random.randint(0, 20) + number = random.choice(the_uglies) + for i in range(turns): + number *= random.choice(self.PRIMES) + assert not is_ugly_number(number) \ No newline at end of file diff --git a/leetcode/math/test_lc273_integer_to_english_words.py b/leetcode/math/test_lc273_integer_to_english_words.py new file mode 100644 index 0000000..5534cb1 --- /dev/null +++ b/leetcode/math/test_lc273_integer_to_english_words.py @@ -0,0 +1,38 @@ +import pytest + +from leetcode.math import lc273_integer_to_english_words + + +class TestNumberToWords(object): + SOLUTION = lc273_integer_to_english_words.Solution() + + def test_zero(self): + number = 0 + assert self.SOLUTION.number_to_words(number) == 'Zero' + + def test_negative(self): + number = -1 + with pytest.raises(ValueError): + self.SOLUTION.number_to_words(number) + + def test_large_number(self): + number = 2**32 + with pytest.raises(ValueError): + self.SOLUTION.number_to_words(number) + + def test_trailing_zero(self): + number = 10000000 + assert self.SOLUTION.number_to_words(number) == 'Ten Million' + + def test_full_of_numbers(self): + number = 2134435666 + assert self.SOLUTION.number_to_words(number) == 'Two Billion One Hundred Thirty Four Million Four Hundred ' \ + 'Thirty Five Thousand Six Hundred Sixty Six' + + def test_below_one_thousand(self): + number = 434 + assert self.SOLUTION.number_to_words(number) == 'Four Hundred Thirty Four' + + def test_zeros(self): + number = 101010101 + assert self.SOLUTION.number_to_words(number) == 'One Hundred One Million Ten Thousand One Hundred One' From 2f09957da40dcce32580cd4df2e6ccdcd54ff447 Mon Sep 17 00:00:00 2001 From: stonemary Date: Sun, 11 Sep 2016 21:44:55 -0700 Subject: [PATCH 2/3] Pylintify code; modify pylintrc --- .pylintrc | 5 ++++- leetcode/math/lc263_ugly_number.py | 8 ++++++++ leetcode/math/lc273_integer_to_english_words.py | 3 +-- ...263_is_ugly_number.py => test_lc263_ugly_number.py} | 10 ++++++---- leetcode/math/test_lc273_integer_to_english_words.py | 2 ++ 5 files changed, 21 insertions(+), 7 deletions(-) rename leetcode/math/{test_lc263_is_ugly_number.py => test_lc263_ugly_number.py} (84%) diff --git a/.pylintrc b/.pylintrc index 84d749e..5a43040 100644 --- a/.pylintrc +++ b/.pylintrc @@ -192,6 +192,9 @@ min-public-methods=0 # Maximum number of public methods for a class (see R0904). max-public-methods=20 +# Minimum line length for functions/classes that require docstrings, shorter ones are exempt. +docstring-min-length=10 + # checks for # * external modules dependencies @@ -263,7 +266,7 @@ notes=FIXME,XXX,TODO [FORMAT] # Maximum number of characters on a single line. -max-line-length=90 +max-line-length=120 # Maximum number of lines in a module max-module-lines=1000 diff --git a/leetcode/math/lc263_ugly_number.py b/leetcode/math/lc263_ugly_number.py index 9451cb5..230f7e7 100644 --- a/leetcode/math/lc263_ugly_number.py +++ b/leetcode/math/lc263_ugly_number.py @@ -9,6 +9,11 @@ def is_ugly_number(number): + """ + Returns True if the number is an ugly number. + :param number: the number + :return: True or False + """ if number <= 0: return False if number == 1: @@ -25,4 +30,7 @@ def is_ugly_number(number): def dividable_by_primes(number, primes): + """ + Returns True if number is dividable by at least one of numbers in the list of `primes` + """ return any(number % prime == 0 for prime in primes) diff --git a/leetcode/math/lc273_integer_to_english_words.py b/leetcode/math/lc273_integer_to_english_words.py index e5a1f61..d9c4bb1 100644 --- a/leetcode/math/lc273_integer_to_english_words.py +++ b/leetcode/math/lc273_integer_to_english_words.py @@ -9,10 +9,9 @@ """ -import math - class Solution(object): + """ The mighty solution - silly pylint requires a docstring """ THOUSAND = 1000 HUNDRED = 100 diff --git a/leetcode/math/test_lc263_is_ugly_number.py b/leetcode/math/test_lc263_ugly_number.py similarity index 84% rename from leetcode/math/test_lc263_is_ugly_number.py rename to leetcode/math/test_lc263_ugly_number.py index 2c60619..090ca01 100644 --- a/leetcode/math/test_lc263_is_ugly_number.py +++ b/leetcode/math/test_lc263_ugly_number.py @@ -1,10 +1,12 @@ -import pytest import random +import pytest + from leetcode.math.lc263_ugly_number import is_ugly_number class TestIsUglyNumber(object): + """ The mighty test suite to the mighty solution. """ PRIMES = [2, 3, 5] @@ -25,7 +27,7 @@ def test_primes(self, test_input): def test_multiple_of_primes(self): turns = random.randint(0, 20) number = 1 - for i in range(turns): + for _ in range(turns): number *= random.choice(self.PRIMES) assert is_ugly_number(number) @@ -33,6 +35,6 @@ def test_ugly_factor(self): the_uglies = [7, 11, 13, 17, 29] turns = random.randint(0, 20) number = random.choice(the_uglies) - for i in range(turns): + for _ in range(turns): number *= random.choice(self.PRIMES) - assert not is_ugly_number(number) \ No newline at end of file + assert not is_ugly_number(number) diff --git a/leetcode/math/test_lc273_integer_to_english_words.py b/leetcode/math/test_lc273_integer_to_english_words.py index 5534cb1..9715b56 100644 --- a/leetcode/math/test_lc273_integer_to_english_words.py +++ b/leetcode/math/test_lc273_integer_to_english_words.py @@ -4,6 +4,8 @@ class TestNumberToWords(object): + """ Test the number to words function. """ + SOLUTION = lc273_integer_to_english_words.Solution() def test_zero(self): From a5f7b8c7162407c3002c62fb884cf7d4c00980b1 Mon Sep 17 00:00:00 2001 From: stonemary Date: Sun, 11 Sep 2016 22:13:34 -0700 Subject: [PATCH 3/3] More pylint fixes --- leetcode/math/test_lc263_ugly_number.py | 5 +++++ leetcode/math/test_lc273_integer_to_english_words.py | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/leetcode/math/test_lc263_ugly_number.py b/leetcode/math/test_lc263_ugly_number.py index 090ca01..53bd290 100644 --- a/leetcode/math/test_lc263_ugly_number.py +++ b/leetcode/math/test_lc263_ugly_number.py @@ -1,3 +1,8 @@ +""" +Test file `lc263_ugly_number.py` +""" +# pylint: disable=no-self-use + import random import pytest diff --git a/leetcode/math/test_lc273_integer_to_english_words.py b/leetcode/math/test_lc273_integer_to_english_words.py index 9715b56..0cb6ca0 100644 --- a/leetcode/math/test_lc273_integer_to_english_words.py +++ b/leetcode/math/test_lc273_integer_to_english_words.py @@ -1,3 +1,7 @@ +""" +Test file `lc273_integer_to_english_words.py` +""" + import pytest from leetcode.math import lc273_integer_to_english_words