From 5e81a98b11f1319b58f5b0d12dbd9b8c648f956a Mon Sep 17 00:00:00 2001 From: duongoku Date: Thu, 22 Jun 2023 00:17:44 +0700 Subject: [PATCH 01/11] Add powersum problem --- backtracking/power_sum.py | 68 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 backtracking/power_sum.py diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py new file mode 100644 index 000000000000..b219099a27dc --- /dev/null +++ b/backtracking/power_sum.py @@ -0,0 +1,68 @@ +""" +Problem source: https://www.hackerrank.com/challenges/the-power-sum/problem +Find the number of ways that a given integer X, can be expressed as the sum +of the Nth powers of unique, natural numbers. For example, if X=13 and N=2. +We have to find all combinations of unique squares adding up to 13. +The only solution is 2^2+3^2. +""" + +from math import pow + +sum_ = 0 +count_ = 0 + + +def backtrack(x: int, n: int, i: int) -> None: + """ + Backtracking function to find all the possible combinations + of powers of natural numbers that add up to x. + Parameters: + x: The number to be expressed as the sum of + the nth powers of unique, natural numbers. + n: The power of the natural numbers. + i: The current natural number. + """ + global sum_, count_ + i_to_n = int(pow(i, n)) + if sum_ == x: + # If the sum of the powers is equal to x, then we have found a solution. + global count_ + count_ += 1 + return + elif sum_ + i_to_n <= x: + # If the sum of the powers is less than x, then we can continue adding powers. + sum_ += i_to_n + backtrack(x, n, i + 1) + sum_ -= i_to_n + if i_to_n < x: + # If the power of i is less than x, then we can try with the next power. + backtrack(x, n, i + 1) + return + + +def solve(x: int, n: int) -> int: + """ + Calculates the number of ways that x can be expressed as the sum of + the nth powers of unique, natural numbers. + Parameters: + x: The number to be expressed as the sum of + the nth powers of unique, natural numbers. + n: The power of the natural numbers. + + Returns: + The number of ways that x can be expressed as the sum of + the nth powers of unique, natural numbers. + """ + global sum_, count_ + backtrack(x, n, 1) + return count_ + + +if __name__ == "__main__": + x = 100 + n = 3 + output = f"The number of ways that {x} can be expressed as the sum of" + output += f" the {n}th powers of unique, natural numbers is {solve(x, n)}" + print(output) + # Output: The number of ways that 100 can be expressed as the + # sum of the 3th powers of unique, natural numbers is 1 From d6c02d33bd75ae651917fcc2bc4e9438222da6c9 Mon Sep 17 00:00:00 2001 From: duongoku Date: Thu, 22 Jun 2023 00:32:41 +0700 Subject: [PATCH 02/11] Add doctest --- backtracking/power_sum.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py index b219099a27dc..343429e9f51f 100644 --- a/backtracking/power_sum.py +++ b/backtracking/power_sum.py @@ -21,6 +21,12 @@ def backtrack(x: int, n: int, i: int) -> None: the nth powers of unique, natural numbers. n: The power of the natural numbers. i: The current natural number. + + Returns: + None + + >>> backtrack(13, 2, 1) + """ global sum_, count_ i_to_n = int(pow(i, n)) @@ -52,17 +58,24 @@ def solve(x: int, n: int) -> int: Returns: The number of ways that x can be expressed as the sum of the nth powers of unique, natural numbers. + + >>> solve(13, 2) + 1 """ global sum_, count_ + sum_, count_ = 0, 0 backtrack(x, n, 1) return count_ if __name__ == "__main__": - x = 100 - n = 3 - output = f"The number of ways that {x} can be expressed as the sum of" - output += f" the {n}th powers of unique, natural numbers is {solve(x, n)}" - print(output) + import doctest + doctest.testmod() + + # x = 100 + # n = 3 + # output = f"The number of ways that {x} can be expressed as the sum of" + # output += f" the {n}th powers of unique, natural numbers is {solve(x, n)}" + # print(output) # Output: The number of ways that 100 can be expressed as the # sum of the 3th powers of unique, natural numbers is 1 From f04de6ab4b71f70feb4165754cf8b5e325fd9d84 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 17:35:58 +0000 Subject: [PATCH 03/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backtracking/power_sum.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py index 343429e9f51f..8021fd3927f1 100644 --- a/backtracking/power_sum.py +++ b/backtracking/power_sum.py @@ -70,8 +70,9 @@ def solve(x: int, n: int) -> int: if __name__ == "__main__": import doctest + doctest.testmod() - + # x = 100 # n = 3 # output = f"The number of ways that {x} can be expressed as the sum of" From 0190498d063f18d5b14c5cfd8e34ec292b287e6a Mon Sep 17 00:00:00 2001 From: duongoku Date: Sun, 25 Jun 2023 19:48:01 +0700 Subject: [PATCH 04/11] Add more doctests --- backtracking/power_sum.py | 63 +++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py index 8021fd3927f1..e945f05297bb 100644 --- a/backtracking/power_sum.py +++ b/backtracking/power_sum.py @@ -3,36 +3,25 @@ Find the number of ways that a given integer X, can be expressed as the sum of the Nth powers of unique, natural numbers. For example, if X=13 and N=2. We have to find all combinations of unique squares adding up to 13. -The only solution is 2^2+3^2. +The only solution is 2^2+3^2. Constraints: 1<=X<=1000, 2<=N<=10. """ from math import pow -sum_ = 0 -count_ = 0 +sum_=count_=0 def backtrack(x: int, n: int, i: int) -> None: """ - Backtracking function to find all the possible combinations - of powers of natural numbers that add up to x. - Parameters: - x: The number to be expressed as the sum of - the nth powers of unique, natural numbers. - n: The power of the natural numbers. - i: The current natural number. - - Returns: - None - - >>> backtrack(13, 2, 1) - + DO NOT CALL THIS METHOD DIRECTLY, USE solve() INSTEAD. + + >>> backtrack(13, 2, 1) is None + True """ global sum_, count_ i_to_n = int(pow(i, n)) if sum_ == x: # If the sum of the powers is equal to x, then we have found a solution. - global count_ count_ += 1 return elif sum_ + i_to_n <= x: @@ -48,20 +37,30 @@ def backtrack(x: int, n: int, i: int) -> None: def solve(x: int, n: int) -> int: """ - Calculates the number of ways that x can be expressed as the sum of - the nth powers of unique, natural numbers. - Parameters: - x: The number to be expressed as the sum of - the nth powers of unique, natural numbers. - n: The power of the natural numbers. - - Returns: - The number of ways that x can be expressed as the sum of - the nth powers of unique, natural numbers. - >>> solve(13, 2) 1 + >>> solve(100, 2) + 3 + >>> solve(100, 3) + 1 + >>> solve(800, 2) + 561 + >>> solve(1000, 10) + 0 + >>> solve(400, 2) + 55 + >>> solve(50, 1) + Traceback (most recent call last): + ... + ValueError: Invalid input, x must be between 1 and 1000, n between 2 and 10. + >>> solve(-10, 5) + Traceback (most recent call last): + ... + ValueError: Invalid input, x must be between 1 and 1000, n between 2 and 10. """ + if not (1 <= x <= 1000 and 2 <= n <= 10): + raise ValueError("Invalid input, x must be between 1 and 1000, n between 2 and 10.") + global sum_, count_ sum_, count_ = 0, 0 backtrack(x, n, 1) @@ -72,11 +71,3 @@ def solve(x: int, n: int) -> int: import doctest doctest.testmod() - - # x = 100 - # n = 3 - # output = f"The number of ways that {x} can be expressed as the sum of" - # output += f" the {n}th powers of unique, natural numbers is {solve(x, n)}" - # print(output) - # Output: The number of ways that 100 can be expressed as the - # sum of the 3th powers of unique, natural numbers is 1 From 12f5f371a8cf95096f01bbf1ffb444fb5b121138 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 25 Jun 2023 12:49:41 +0000 Subject: [PATCH 05/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backtracking/power_sum.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py index e945f05297bb..83ee0a0885c0 100644 --- a/backtracking/power_sum.py +++ b/backtracking/power_sum.py @@ -8,13 +8,13 @@ from math import pow -sum_=count_=0 +sum_ = count_ = 0 def backtrack(x: int, n: int, i: int) -> None: """ DO NOT CALL THIS METHOD DIRECTLY, USE solve() INSTEAD. - + >>> backtrack(13, 2, 1) is None True """ @@ -59,7 +59,9 @@ def solve(x: int, n: int) -> int: ValueError: Invalid input, x must be between 1 and 1000, n between 2 and 10. """ if not (1 <= x <= 1000 and 2 <= n <= 10): - raise ValueError("Invalid input, x must be between 1 and 1000, n between 2 and 10.") + raise ValueError( + "Invalid input, x must be between 1 and 1000, n between 2 and 10." + ) global sum_, count_ sum_, count_ = 0, 0 From 6ac94146ab640316631e7bd8ce484f00112496bb Mon Sep 17 00:00:00 2001 From: duongoku Date: Sun, 25 Jun 2023 20:00:37 +0700 Subject: [PATCH 06/11] Add more doctests --- backtracking/power_sum.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py index 83ee0a0885c0..1b568e041022 100644 --- a/backtracking/power_sum.py +++ b/backtracking/power_sum.py @@ -17,6 +17,8 @@ def backtrack(x: int, n: int, i: int) -> None: >>> backtrack(13, 2, 1) is None True + >>> backtrack(1000, 10, 2) is None + True """ global sum_, count_ i_to_n = int(pow(i, n)) From d4e18f113f337b2fe77fbed1685ed42ee968f3d2 Mon Sep 17 00:00:00 2001 From: duongoku Date: Sun, 25 Jun 2023 21:32:32 +0700 Subject: [PATCH 07/11] Improve paramater name --- backtracking/power_sum.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py index 1b568e041022..b0872bb4762e 100644 --- a/backtracking/power_sum.py +++ b/backtracking/power_sum.py @@ -11,7 +11,7 @@ sum_ = count_ = 0 -def backtrack(x: int, n: int, i: int) -> None: +def backtrack(needed_sum: int, power: int, current_number: int) -> None: """ DO NOT CALL THIS METHOD DIRECTLY, USE solve() INSTEAD. @@ -21,23 +21,23 @@ def backtrack(x: int, n: int, i: int) -> None: True """ global sum_, count_ - i_to_n = int(pow(i, n)) - if sum_ == x: - # If the sum of the powers is equal to x, then we have found a solution. + i_to_n = int(pow(current_number, power)) + if sum_ == needed_sum: + # If the sum of the powers is equal to needed_sum, then we have found a solution. count_ += 1 return - elif sum_ + i_to_n <= x: - # If the sum of the powers is less than x, then we can continue adding powers. + elif sum_ + i_to_n <= needed_sum: + # If the sum of the powers is less than needed_sum, then we can continue adding powers. sum_ += i_to_n - backtrack(x, n, i + 1) + backtrack(needed_sum, power, current_number + 1) sum_ -= i_to_n - if i_to_n < x: - # If the power of i is less than x, then we can try with the next power. - backtrack(x, n, i + 1) + if i_to_n < needed_sum: + # If the power of i is less than needed_sum, then we can try with the next power. + backtrack(needed_sum, power, current_number + 1) return -def solve(x: int, n: int) -> int: +def solve(needed_sum: int, power: int) -> int: """ >>> solve(13, 2) 1 @@ -54,20 +54,20 @@ def solve(x: int, n: int) -> int: >>> solve(50, 1) Traceback (most recent call last): ... - ValueError: Invalid input, x must be between 1 and 1000, n between 2 and 10. + ValueError: Invalid input, needed_sum must be between 1 and 1000, power between 2 and 10. >>> solve(-10, 5) Traceback (most recent call last): ... - ValueError: Invalid input, x must be between 1 and 1000, n between 2 and 10. + ValueError: Invalid input, needed_sum must be between 1 and 1000, power between 2 and 10. """ - if not (1 <= x <= 1000 and 2 <= n <= 10): + if not (1 <= needed_sum <= 1000 and 2 <= power <= 10): raise ValueError( - "Invalid input, x must be between 1 and 1000, n between 2 and 10." + "Invalid input, needed_sum must be between 1 and 1000, power between 2 and 10." ) global sum_, count_ sum_, count_ = 0, 0 - backtrack(x, n, 1) + backtrack(needed_sum, power, 1) return count_ From 586a4cf99838e2eef22ee22e85c32dc0f1f5eae2 Mon Sep 17 00:00:00 2001 From: duongoku Date: Sun, 25 Jun 2023 21:37:59 +0700 Subject: [PATCH 08/11] Fix line too long --- backtracking/power_sum.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py index b0872bb4762e..0504bc4461a4 100644 --- a/backtracking/power_sum.py +++ b/backtracking/power_sum.py @@ -23,16 +23,19 @@ def backtrack(needed_sum: int, power: int, current_number: int) -> None: global sum_, count_ i_to_n = int(pow(current_number, power)) if sum_ == needed_sum: - # If the sum of the powers is equal to needed_sum, then we have found a solution. + # If the sum of the powers is equal to needed_sum, + # then we have found a solution. count_ += 1 return elif sum_ + i_to_n <= needed_sum: - # If the sum of the powers is less than needed_sum, then we can continue adding powers. + # If the sum of the powers is less than needed_sum, + # then we can continue adding powers. sum_ += i_to_n backtrack(needed_sum, power, current_number + 1) sum_ -= i_to_n if i_to_n < needed_sum: - # If the power of i is less than needed_sum, then we can try with the next power. + # If the power of i is less than needed_sum, + # then we can try with the next power. backtrack(needed_sum, power, current_number + 1) return @@ -54,15 +57,18 @@ def solve(needed_sum: int, power: int) -> int: >>> solve(50, 1) Traceback (most recent call last): ... - ValueError: Invalid input, needed_sum must be between 1 and 1000, power between 2 and 10. + ValueError: Invalid input + needed_sum must be between 1 and 1000, power between 2 and 10. >>> solve(-10, 5) Traceback (most recent call last): ... - ValueError: Invalid input, needed_sum must be between 1 and 1000, power between 2 and 10. + ValueError: Invalid input + needed_sum must be between 1 and 1000, power between 2 and 10. """ if not (1 <= needed_sum <= 1000 and 2 <= power <= 10): raise ValueError( - "Invalid input, needed_sum must be between 1 and 1000, power between 2 and 10." + "Invalid input\n" + "needed_sum must be between 1 and 1000, power between 2 and 10." ) global sum_, count_ From ef288b0a260846419d637e31c077fc9699c6338e Mon Sep 17 00:00:00 2001 From: duongoku Date: Mon, 26 Jun 2023 08:11:37 +0700 Subject: [PATCH 09/11] Remove global variables --- backtracking/power_sum.py | 57 ++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py index 0504bc4461a4..4bc04b9f3f45 100644 --- a/backtracking/power_sum.py +++ b/backtracking/power_sum.py @@ -8,36 +8,51 @@ from math import pow -sum_ = count_ = 0 - -def backtrack(needed_sum: int, power: int, current_number: int) -> None: +def backtrack( + needed_sum: int, + power: int, + current_number: int, + current_sum: int, + solutions_count: int, +) -> tuple[int, int]: """ - DO NOT CALL THIS METHOD DIRECTLY, USE solve() INSTEAD. - - >>> backtrack(13, 2, 1) is None - True - >>> backtrack(1000, 10, 2) is None - True + >>> backtrack(13, 2, 1, 0, 0) + (0, 1) + >>> backtrack(100, 2, 1, 0, 0) + (0, 3) + >>> backtrack(100, 3, 1, 0, 0) + (0, 1) + >>> backtrack(800, 2, 1, 0, 0) + (0, 561) + >>> backtrack(1000, 10, 1, 0, 0) + (0, 0) + >>> backtrack(400, 2, 1, 0, 0) + (0, 55) + >>> backtrack(50, 1, 1, 0, 0) + (0, 3658) """ - global sum_, count_ i_to_n = int(pow(current_number, power)) - if sum_ == needed_sum: + if current_sum == needed_sum: # If the sum of the powers is equal to needed_sum, # then we have found a solution. - count_ += 1 - return - elif sum_ + i_to_n <= needed_sum: + solutions_count += 1 + return current_sum, solutions_count + elif current_sum + i_to_n <= needed_sum: # If the sum of the powers is less than needed_sum, # then we can continue adding powers. - sum_ += i_to_n - backtrack(needed_sum, power, current_number + 1) - sum_ -= i_to_n + current_sum += i_to_n + current_sum, solutions_count = backtrack( + needed_sum, power, current_number + 1, current_sum, solutions_count + ) + current_sum -= i_to_n if i_to_n < needed_sum: # If the power of i is less than needed_sum, # then we can try with the next power. - backtrack(needed_sum, power, current_number + 1) - return + current_sum, solutions_count = backtrack( + needed_sum, power, current_number + 1, current_sum, solutions_count + ) + return current_sum, solutions_count def solve(needed_sum: int, power: int) -> int: @@ -71,9 +86,7 @@ def solve(needed_sum: int, power: int) -> int: "needed_sum must be between 1 and 1000, power between 2 and 10." ) - global sum_, count_ - sum_, count_ = 0, 0 - backtrack(needed_sum, power, 1) + _, count_ = backtrack(needed_sum, power, 1, 0, 0) return count_ From 990f1150e24f2d178de2fed7d91931167b1be4c0 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 26 Jun 2023 09:27:35 +0200 Subject: [PATCH 10/11] Apply suggestions from code review --- backtracking/power_sum.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py index 4bc04b9f3f45..5df748cc9466 100644 --- a/backtracking/power_sum.py +++ b/backtracking/power_sum.py @@ -32,23 +32,21 @@ def backtrack( >>> backtrack(50, 1, 1, 0, 0) (0, 3658) """ - i_to_n = int(pow(current_number, power)) if current_sum == needed_sum: - # If the sum of the powers is equal to needed_sum, - # then we have found a solution. + # If the sum of the powers is equal to needed_sum, then we have found a solution. solutions_count += 1 return current_sum, solutions_count - elif current_sum + i_to_n <= needed_sum: - # If the sum of the powers is less than needed_sum, - # then we can continue adding powers. + + i_to_n = int(pow(current_number, power)) + if current_sum + i_to_n <= needed_sum: + # If the sum of the powers is less than needed_sum, then we can continue adding powers. current_sum += i_to_n current_sum, solutions_count = backtrack( needed_sum, power, current_number + 1, current_sum, solutions_count ) current_sum -= i_to_n if i_to_n < needed_sum: - # If the power of i is less than needed_sum, - # then we can try with the next power. + # If the power of i is less than needed_sum, then we can try with the next power. current_sum, solutions_count = backtrack( needed_sum, power, current_number + 1, current_sum, solutions_count ) @@ -86,8 +84,7 @@ def solve(needed_sum: int, power: int) -> int: "needed_sum must be between 1 and 1000, power between 2 and 10." ) - _, count_ = backtrack(needed_sum, power, 1, 0, 0) - return count_ + return backtrack(needed_sum, power, 1, 0, 0)[1] # Return the solutions_count if __name__ == "__main__": From 70322f8b34bf6c3948b37dcf6a9b77cce9a46afc Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 26 Jun 2023 09:34:36 +0200 Subject: [PATCH 11/11] Apply suggestions from code review --- backtracking/power_sum.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backtracking/power_sum.py b/backtracking/power_sum.py index 5df748cc9466..fcf1429f8570 100644 --- a/backtracking/power_sum.py +++ b/backtracking/power_sum.py @@ -33,20 +33,20 @@ def backtrack( (0, 3658) """ if current_sum == needed_sum: - # If the sum of the powers is equal to needed_sum, then we have found a solution. + # If the sum of the powers is equal to needed_sum, then we have a solution. solutions_count += 1 return current_sum, solutions_count i_to_n = int(pow(current_number, power)) if current_sum + i_to_n <= needed_sum: - # If the sum of the powers is less than needed_sum, then we can continue adding powers. + # If the sum of the powers is less than needed_sum, then continue adding powers. current_sum += i_to_n current_sum, solutions_count = backtrack( needed_sum, power, current_number + 1, current_sum, solutions_count ) current_sum -= i_to_n if i_to_n < needed_sum: - # If the power of i is less than needed_sum, then we can try with the next power. + # If the power of i is less than needed_sum, then try with the next power. current_sum, solutions_count = backtrack( needed_sum, power, current_number + 1, current_sum, solutions_count )