From 693f6f088a62472b887a899909a49161d132d83e Mon Sep 17 00:00:00 2001 From: KumarUniverse Date: Thu, 15 Oct 2020 14:27:07 -0400 Subject: [PATCH 1/9] Added solution to Project Euler problem 301 --- project_euler/problem_301/__init__.py | 0 project_euler/problem_301/sol1.py | 74 +++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 project_euler/problem_301/__init__.py create mode 100644 project_euler/problem_301/sol1.py diff --git a/project_euler/problem_301/__init__.py b/project_euler/problem_301/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_301/sol1.py b/project_euler/problem_301/sol1.py new file mode 100644 index 000000000000..b8020181f34c --- /dev/null +++ b/project_euler/problem_301/sol1.py @@ -0,0 +1,74 @@ +""" +Project Euler Problem 301: https://projecteuler.net/problem=301 + +Problem Statement: +Nim is a game played with heaps of stones, where two players take +it in turn to remove any number of stones from any heap until no stones remain. + +We'll consider the three-heap normal-play version of +Nim, which works as follows: +- At the start of the game there are three heaps of stones. +- On each player's turn, the player may remove any positive + number of stones from any single heap. +- The first player unable to move (because no stones remain) loses. + +If (n1, n2, n3) indicates a Nim position consisting of heaps of size +n1, n2, and n3, then there is a simple function, which you may look up +or attempt to deduce for yourself, X(n1, n2, n3) that returns: +- zero if, with perfect strategy, the player about to + move will eventually lose; or +- non-zero if, with perfect strategy, the player about + to move will eventually win. + +For example X(1,2,3) = 0 because, no matter what the current player does, +the opponent can respond with a move that leaves two heaps of equal size, +at which point every move by the current player can be mirrored by the +opponent until no stones remain; so the current player loses. To illustrate: +- current player moves to (1,2,1) +- opponent moves to (1,0,1) +- current player moves to (0,0,1) +- opponent moves to (0,0,0), and so wins. + +For how many positive integers n <= 2^30 does X(n,2n,3n) = 0? +""" + +def X(n: int, n2: int, n3: int) -> int: + """ + Returns: + - zero if, with perfect strategy, the player about to + move will eventually lose; or + - non-zero if, with perfect strategy, the player about + to move will eventually win. + + >>> X(1) + 0 + >>> X(3) + 12 + >>> X(8) + 0 + >>> X(11) + 60 + >>> X(1000) + 3968 + """ + return n ^ n2 ^ n3 + +def solution(n: int = 2**30) -> int: + """ + For a given integer n <= 2^30, returns how many Nim games are lost. + >>> solution(2) + 2 + >>> solution(10) + 144 + >>> solution(30) + 2178309 + """ + lossCount = 0 + for i in range(1,n+1): + if X(i,2*i,3*i) == 0: + lossCount += 1 + + return lossCount + +if __name__ == "__main__": + print(solution()) \ No newline at end of file From fb35f2265c8f458b13824ca56f321fe63cde303f Mon Sep 17 00:00:00 2001 From: KumarUniverse Date: Thu, 15 Oct 2020 15:21:57 -0400 Subject: [PATCH 2/9] Added newline to end of file --- project_euler/problem_301/sol1.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/project_euler/problem_301/sol1.py b/project_euler/problem_301/sol1.py index b8020181f34c..3cf720b2a77a 100644 --- a/project_euler/problem_301/sol1.py +++ b/project_euler/problem_301/sol1.py @@ -32,7 +32,7 @@ For how many positive integers n <= 2^30 does X(n,2n,3n) = 0? """ -def X(n: int, n2: int, n3: int) -> int: +def x(n: int, n2: int, n3: int) -> int: """ Returns: - zero if, with perfect strategy, the player about to @@ -40,15 +40,15 @@ def X(n: int, n2: int, n3: int) -> int: - non-zero if, with perfect strategy, the player about to move will eventually win. - >>> X(1) + >>> x(1) 0 - >>> X(3) + >>> x(3) 12 - >>> X(8) + >>> x(8) 0 - >>> X(11) + >>> x(11) 60 - >>> X(1000) + >>> x(1000) 3968 """ return n ^ n2 ^ n3 @@ -65,10 +65,10 @@ def solution(n: int = 2**30) -> int: """ lossCount = 0 for i in range(1,n+1): - if X(i,2*i,3*i) == 0: + if x(i,2*i,3*i) == 0: lossCount += 1 return lossCount if __name__ == "__main__": - print(solution()) \ No newline at end of file + print(solution()) From e04b91f020743e89dc591b83be27a0f061b636f8 Mon Sep 17 00:00:00 2001 From: KumarUniverse Date: Thu, 15 Oct 2020 15:58:41 -0400 Subject: [PATCH 3/9] Fixed formatting and tests --- project_euler/problem_301/sol1.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/project_euler/problem_301/sol1.py b/project_euler/problem_301/sol1.py index 3cf720b2a77a..afd4f36ecaed 100644 --- a/project_euler/problem_301/sol1.py +++ b/project_euler/problem_301/sol1.py @@ -32,6 +32,7 @@ For how many positive integers n <= 2^30 does X(n,2n,3n) = 0? """ + def x(n: int, n2: int, n3: int) -> int: """ Returns: @@ -40,35 +41,35 @@ def x(n: int, n2: int, n3: int) -> int: - non-zero if, with perfect strategy, the player about to move will eventually win. - >>> x(1) + >>> x(1, 2, 3) 0 - >>> x(3) + >>> x(3, 6, 9) 12 - >>> x(8) + >>> x(8, 16, 24) 0 - >>> x(11) + >>> x(11, 22, 33) 60 - >>> x(1000) + >>> x(1000, 2000, 3000) 3968 """ return n ^ n2 ^ n3 -def solution(n: int = 2**30) -> int: + +def solution(n: int = 2 ** 10) -> int: """ For a given integer n <= 2^30, returns how many Nim games are lost. >>> solution(2) 2 - >>> solution(10) + >>> solution(2 ** 10) 144 - >>> solution(30) - 2178309 """ lossCount = 0 - for i in range(1,n+1): - if x(i,2*i,3*i) == 0: + for i in range(1, n + 1): + if x(i, 2 * i, 3 * i) == 0: lossCount += 1 return lossCount + if __name__ == "__main__": print(solution()) From 4a08a56cd551ed5a7d8f0f6aec22c7bdf48762be Mon Sep 17 00:00:00 2001 From: KumarUniverse Date: Thu, 15 Oct 2020 16:09:44 -0400 Subject: [PATCH 4/9] Changed lossCount to loss_count --- project_euler/problem_301/sol1.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/project_euler/problem_301/sol1.py b/project_euler/problem_301/sol1.py index afd4f36ecaed..96ec549f69df 100644 --- a/project_euler/problem_301/sol1.py +++ b/project_euler/problem_301/sol1.py @@ -63,12 +63,12 @@ def solution(n: int = 2 ** 10) -> int: >>> solution(2 ** 10) 144 """ - lossCount = 0 + loss_count = 0 for i in range(1, n + 1): if x(i, 2 * i, 3 * i) == 0: - lossCount += 1 + loss_count += 1 - return lossCount + return loss_count if __name__ == "__main__": From c902c36a8093d3efe5e7b2f3914ce3c39f4cada7 Mon Sep 17 00:00:00 2001 From: KumarUniverse Date: Thu, 15 Oct 2020 19:24:57 -0400 Subject: [PATCH 5/9] Fixed default parameter value for solution --- project_euler/problem_301/sol1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_euler/problem_301/sol1.py b/project_euler/problem_301/sol1.py index 96ec549f69df..fba4dae0d759 100644 --- a/project_euler/problem_301/sol1.py +++ b/project_euler/problem_301/sol1.py @@ -55,7 +55,7 @@ def x(n: int, n2: int, n3: int) -> int: return n ^ n2 ^ n3 -def solution(n: int = 2 ** 10) -> int: +def solution(n: int = 2 ** 30) -> int: """ For a given integer n <= 2^30, returns how many Nim games are lost. >>> solution(2) From e920501f800f9b9330a4d7efe9c06866eb9859cf Mon Sep 17 00:00:00 2001 From: KumarUniverse Date: Fri, 16 Oct 2020 10:32:27 -0400 Subject: [PATCH 6/9] Removed helper function and modified print stmt --- project_euler/problem_301/sol1.py | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/project_euler/problem_301/sol1.py b/project_euler/problem_301/sol1.py index fba4dae0d759..fb442c85d5d1 100644 --- a/project_euler/problem_301/sol1.py +++ b/project_euler/problem_301/sol1.py @@ -33,28 +33,6 @@ """ -def x(n: int, n2: int, n3: int) -> int: - """ - Returns: - - zero if, with perfect strategy, the player about to - move will eventually lose; or - - non-zero if, with perfect strategy, the player about - to move will eventually win. - - >>> x(1, 2, 3) - 0 - >>> x(3, 6, 9) - 12 - >>> x(8, 16, 24) - 0 - >>> x(11, 22, 33) - 60 - >>> x(1000, 2000, 3000) - 3968 - """ - return n ^ n2 ^ n3 - - def solution(n: int = 2 ** 30) -> int: """ For a given integer n <= 2^30, returns how many Nim games are lost. @@ -65,11 +43,11 @@ def solution(n: int = 2 ** 30) -> int: """ loss_count = 0 for i in range(1, n + 1): - if x(i, 2 * i, 3 * i) == 0: + if (i^(2*i)^(3*i)) == 0: loss_count += 1 return loss_count if __name__ == "__main__": - print(solution()) + print(f"{solution() = }") From 96e6a1f21503a535cb7cedd3e01d06df52a3edcc Mon Sep 17 00:00:00 2001 From: KumarUniverse Date: Fri, 16 Oct 2020 11:12:17 -0400 Subject: [PATCH 7/9] Fixed code formatting --- project_euler/problem_301/sol1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_euler/problem_301/sol1.py b/project_euler/problem_301/sol1.py index fb442c85d5d1..853c5ab7b143 100644 --- a/project_euler/problem_301/sol1.py +++ b/project_euler/problem_301/sol1.py @@ -43,7 +43,7 @@ def solution(n: int = 2 ** 30) -> int: """ loss_count = 0 for i in range(1, n + 1): - if (i^(2*i)^(3*i)) == 0: + if (i ^ (2 * i) ^ (3 * i)) == 0: loss_count += 1 return loss_count From a42997e16f3407505cb9f23a0c2e4357fdc8bc35 Mon Sep 17 00:00:00 2001 From: KumarUniverse Date: Fri, 16 Oct 2020 12:22:43 -0400 Subject: [PATCH 8/9] Optimized solution from O(n^2) to O(1) constant time --- project_euler/problem_301/sol1.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/project_euler/problem_301/sol1.py b/project_euler/problem_301/sol1.py index 853c5ab7b143..31d4fddb1365 100644 --- a/project_euler/problem_301/sol1.py +++ b/project_euler/problem_301/sol1.py @@ -33,20 +33,28 @@ """ -def solution(n: int = 2 ** 30) -> int: +def solution(exponent: int = 30) -> int: """ - For a given integer n <= 2^30, returns how many Nim games are lost. + For any given exponent x >= 0, 1 <= n <= 2^x. + This function returns how many Nim games are lost given that + each Nim game has three heaps of the form (n, 2*n, 3*n). + >>> solution(0) + 1 >>> solution(2) - 2 - >>> solution(2 ** 10) + 3 + >>> solution(10) 144 + >>> solution(30) + 2178309 """ - loss_count = 0 - for i in range(1, n + 1): - if (i ^ (2 * i) ^ (3 * i)) == 0: - loss_count += 1 - - return loss_count + # To find how many total games were lost for a given exponent x, + # we need to find the Fibonacci number F(x+2). + fib_ind = exponent + 2 + phi = (1 + 5 ** 0.5) / 2 + fib = (phi ** fib_ind - (phi - 1) ** fib_ind) / 5 ** 0.5 + nim_loss_count = int(fib) + + return nim_loss_count if __name__ == "__main__": From 0cbfa2789a12fe39cec57dadf59a8f6917cf36b9 Mon Sep 17 00:00:00 2001 From: Akash Kumar Date: Mon, 26 Oct 2020 09:47:38 -0400 Subject: [PATCH 9/9] Update sol1.py --- project_euler/problem_301/sol1.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/project_euler/problem_301/sol1.py b/project_euler/problem_301/sol1.py index 31d4fddb1365..b1d434c189b7 100644 --- a/project_euler/problem_301/sol1.py +++ b/project_euler/problem_301/sol1.py @@ -44,17 +44,14 @@ def solution(exponent: int = 30) -> int: 3 >>> solution(10) 144 - >>> solution(30) - 2178309 """ # To find how many total games were lost for a given exponent x, # we need to find the Fibonacci number F(x+2). - fib_ind = exponent + 2 + fibonacci_index = exponent + 2 phi = (1 + 5 ** 0.5) / 2 - fib = (phi ** fib_ind - (phi - 1) ** fib_ind) / 5 ** 0.5 - nim_loss_count = int(fib) + fibonacci = (phi ** fibonacci_index - (phi - 1) ** fibonacci_index) / 5 ** 0.5 - return nim_loss_count + return int(fibonacci) if __name__ == "__main__":