From cabc99ce3932f38d96319ce462a6924c1dbbe0a5 Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Sun, 20 Mar 2022 07:35:33 +0900 Subject: [PATCH 1/6] Request level5/disorderly-escape --- level5/disorderly-escape/constraints.txt | 21 ++++++ level5/disorderly-escape/readme.txt | 84 ++++++++++++++++++++++++ level5/disorderly-escape/solution.py | 13 ++++ 3 files changed, 118 insertions(+) create mode 100644 level5/disorderly-escape/constraints.txt create mode 100644 level5/disorderly-escape/readme.txt create mode 100644 level5/disorderly-escape/solution.py diff --git a/level5/disorderly-escape/constraints.txt b/level5/disorderly-escape/constraints.txt new file mode 100644 index 0000000..f201987 --- /dev/null +++ b/level5/disorderly-escape/constraints.txt @@ -0,0 +1,21 @@ +Java +==== +Your code will be compiled using standard Java 8. All tests will be run by calling the solution() method inside the Solution class + +Execution time is limited. + +Wildcard imports and some specific classes are restricted (e.g. java.lang.ClassLoader). You will receive an error when you verify your solution if you have used a blacklisted class. + +Third-party libraries, input/output operations, spawning threads or processes and changes to the execution environment are not allowed. + +Your solution must be under 32000 characters in length including new lines and and other non-printing characters. + +Python +====== +Your code will run inside a Python 2.7.13 sandbox. All tests will be run by calling the solution() function. + +Standard libraries are supported except for bz2, crypt, fcntl, mmap, pwd, pyexpat, select, signal, termios, thread, time, unicodedata, zipimport, zlib. + +Input/output operations are not allowed. + +Your solution must be under 32000 characters in length including new lines and and other non-printing characters. diff --git a/level5/disorderly-escape/readme.txt b/level5/disorderly-escape/readme.txt new file mode 100644 index 0000000..704d91a --- /dev/null +++ b/level5/disorderly-escape/readme.txt @@ -0,0 +1,84 @@ +Disorderly Escape +================= + +Oh no! You've managed to free the bunny workers and escape Commander Lambdas exploding space station, but Lambda's team of elite starfighters has flanked your ship. If you dont jump to hyperspace, and fast, youll be shot out of the sky! + +Problem is, to avoid detection by galactic law enforcement, Commander Lambda planted the space station in the middle of a quasar quantum flux field. In order to make the jump to hyperspace, you need to know the configuration of celestial bodies in the quadrant you plan to jump through. In order to do *that*, you need to figure out how many configurations each quadrant could possibly have, so that you can pick the optimal quadrant through which youll make your jump. + +There's something important to note about quasar quantum flux fields' configurations: when drawn on a star grid, configurations are considered equivalent by grouping rather than by order. That is, for a given set of configurations, if you exchange the position of any two columns or any two rows some number of times, youll find that all of those configurations are equivalent in that way -- in grouping, rather than order. + +Write a function solution(w, h, s) that takes 3 integers and returns the number of unique, non-equivalent configurations that can be found on a star grid w blocks wide and h blocks tall where each celestial body has s possible states. Equivalency is defined as above: any two star grids with each celestial body in the same state where the actual order of the rows and columns do not matter (and can thus be freely swapped around). Star grid standardization means that the width and height of the grid will always be between 1 and 12, inclusive. And while there are a variety of celestial bodies in each grid, the number of states of those bodies is between 2 and 20, inclusive. The solution can be over 20 digits long, so return it as a decimal string. The intermediate values can also be large, so you will likely need to use at least 64-bit integers. + +For example, consider w=2, h=2, s=2. We have a 2x2 grid where each celestial body is either in state 0 (for instance, silent) or state 1 (for instance, noisy). We can examine which grids are equivalent by swapping rows and columns. + +00 +00 + +In the above configuration, all celestial bodies are "silent" - that is, they have a state of 0 - so any swap of row or column would keep it in the same state. + +00 00 01 10 +01 10 00 00 + +1 celestial body is emitting noise - that is, has a state of 1 - so swapping rows and columns can put it in any of the 4 positions. All four of the above configurations are equivalent. + +00 11 +11 00 + +2 celestial bodies are emitting noise side-by-side. Swapping columns leaves them unchanged, and swapping rows simply moves them between the top and bottom. In both, the *groupings* are the same: one row with two bodies in state 0, one row with two bodies in state 1, and two columns with one of each state. + +01 10 +01 10 + +2 noisy celestial bodies adjacent vertically. This is symmetric to the side-by-side case, but it is different because there's no way to transpose the grid. + +01 10 +10 01 + +2 noisy celestial bodies diagonally. Both have 2 rows and 2 columns that have one of each state, so they are equivalent to each other. + +01 10 11 11 +11 11 01 10 + +3 noisy celestial bodies, similar to the case where only one of four is noisy. + +11 +11 + +4 noisy celestial bodies. + +There are 7 distinct, non-equivalent grids in total, so solution(2, 2, 2) would return 7. + +Languages +========= + +To provide a Java solution, edit Solution.java +To provide a Python solution, edit solution.py + +Test cases +========== +Your code should pass the following test cases. +Note that it may also be run against hidden test cases not shown here. + +-- Java cases -- +Input: +Solution.solution(2, 3, 4) +Output: + 430 + +Input: +Solution.solution(2, 2, 2) +Output: + 7 + +-- Python cases -- +Input: +solution.solution(2, 3, 4) +Output: + 430 + +Input: +solution.solution(2, 2, 2) +Output: + 7 + +Use verify [file] to test your solution and see how it does. When you are finished editing your code, use submit [file] to submit your answer. If your solution passes the test cases, it will be removed from your home folder. diff --git a/level5/disorderly-escape/solution.py b/level5/disorderly-escape/solution.py new file mode 100644 index 0000000..b28a30b --- /dev/null +++ b/level5/disorderly-escape/solution.py @@ -0,0 +1,13 @@ +def solution(w, h, s): + return '0' + +tests = [ + ([2, 3, 4], '430'), + ([2, 2, 2], '7'), + ] + +for i, o in tests: + result = solution(*i) + print (i, result == o, result, o) + + -- 2.47.2 From c4efc8d7d1aebecb2f0518016c14bf9da70bc278 Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Sun, 20 Mar 2022 07:38:10 +0900 Subject: [PATCH 2/6] Using Burnside lemma Need to implement counting the cycles for each combination * Reference: https://youtu.be/D0d9bYZ_qDY --- level5/disorderly-escape/solution.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/level5/disorderly-escape/solution.py b/level5/disorderly-escape/solution.py index b28a30b..9f8a63c 100644 --- a/level5/disorderly-escape/solution.py +++ b/level5/disorderly-escape/solution.py @@ -1,9 +1,22 @@ +import math + def solution(w, h, s): - return '0' + return str(sum([s**cycle for cycle in generate_cycles(w, h)])/(math.factorial(w)*math.factorial(h))) + +def generate_cycles(w, h): + yield 1 + tests = [ ([2, 3, 4], '430'), ([2, 2, 2], '7'), + ([1, 1, 2], '2'), + ([1, 1, 3], '3'), + ([2, 1, 2], '3'), + ([1, 2, 2], '3'), + ([3, 1, 2], '4'), + ([1, 3, 2], '4'), + ([2, 3, 2], '13'), ] for i, o in tests: -- 2.47.2 From e8f69a8743b181de95c5b076be28b5748f082ab5 Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Mon, 21 Mar 2022 05:35:25 +0900 Subject: [PATCH 3/6] Count cycles by iteration Verifying solution... Test 1 passed! Test 2 passed! Test 3 failed [Hidden] Test 4 passed! [Hidden] Test 5 failed [Hidden] Test 6 failed [Hidden] Test 7 failed [Hidden] Test 8 failed [Hidden] Test 9 passed! [Hidden] Test 10 failed [Hidden] --- level5/disorderly-escape/solution.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/level5/disorderly-escape/solution.py b/level5/disorderly-escape/solution.py index 9f8a63c..2921b7b 100644 --- a/level5/disorderly-escape/solution.py +++ b/level5/disorderly-escape/solution.py @@ -1,11 +1,36 @@ import math +import numpy as np def solution(w, h, s): return str(sum([s**cycle for cycle in generate_cycles(w, h)])/(math.factorial(w)*math.factorial(h))) def generate_cycles(w, h): - yield 1 + identity = np.arange(w*h).reshape((w, h)) + for i in generate_permutation(list(range(w))): + for j in generate_permutation(list(range(h))): + yield count_cycle(identity[i][:, j]) +def generate_permutation(numbers): + if len(numbers) == 1: + yield numbers + else: + for i, n in enumerate(numbers): + for j in generate_permutation(numbers[:i] + numbers[i+1:]): + yield [n] + j + +def count_cycle(arr): + cycle = np.zeros(arr.shape, dtype=int) + w, h = arr.shape + cycle_id = 0 + for i in range(w): + for j in range(h): + x, y = i, j + if cycle[x, y] == 0: + cycle_id += 1 + while cycle[x, y] == 0: + cycle[x, y] = cycle_id + x, y = arr[x, y]/h, arr[x, y]%h + return cycle_id tests = [ ([2, 3, 4], '430'), -- 2.47.2 From 7b386ff8510f2d4e9318b3c0f4756135e613a267 Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Mon, 21 Mar 2022 15:56:31 +0900 Subject: [PATCH 4/6] Use DP to enhance the performance Verifying solution... Test 1 passed! Test 2 passed! Test 3 passed! [Hidden] Test 4 passed! [Hidden] Test 5 failed [Hidden] Test 6 failed [Hidden] Test 7 passed! [Hidden] Test 8 failed [Hidden] Test 9 passed! [Hidden] Test 10 failed [Hidden] --- level5/disorderly-escape/solution.py | 136 +++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 20 deletions(-) diff --git a/level5/disorderly-escape/solution.py b/level5/disorderly-escape/solution.py index 2921b7b..3c8b92e 100644 --- a/level5/disorderly-escape/solution.py +++ b/level5/disorderly-escape/solution.py @@ -2,23 +2,106 @@ import math import numpy as np def solution(w, h, s): - return str(sum([s**cycle for cycle in generate_cycles(w, h)])/(math.factorial(w)*math.factorial(h))) + memo_f = {w:math.factorial(w), h:math.factorial(h)} + memo_sp = {} + cases = 0 + for c in generate_cycle_count(w, h, memo_f): + if c not in memo_sp: + memo_sp[c] = s ** c + cases += memo_sp[c] + return str(cases / (memo_f[w] * memo_f[h])) -def generate_cycles(w, h): +def generate_cycle_count(w, h, memo_f = {}): + memo_c = {} + memo_p = {} + memo_gcd = {} + for pw in generate_cycles(w, memo = memo_c): + for ph in generate_cycles(h, memo = memo_c): + cycle_count = 0 + for i in pw.split(): + for j in ph.split(): + cycle_count += gcd(i, j, memo_gcd) + cases = count_permutation(pw, memo_f, memo_p) * count_permutation(ph, memo_f, memo_p) + #print pw, ph, cases, cycle_count + for i in range(cases): + yield cycle_count + +# generate cycle list in a string format +# e.g., a string with length 5 can be composed with '2 2 1' which means there are 3 cycles, and foreach size is 2, 2 and 1. +def generate_cycles(remain, start = 1, memo = {}): + if remain < start: + return + if (remain, start) in memo: + for j in memo[(remain, start)]: + yield j + else: + memo[(remain, start)] = [] + for i in range(start, remain+1): + if i == remain: + memo[(remain, start)] += [str(i)] + for j in generate_cycles(remain - i, i, memo): + memo[(remain, start)] += [str(i) + ' ' + j] + for j in memo[(remain, start)]: + yield j + +def gcd(x, y, memo = {}): + if (x, y) in memo: + return memo[(x, y)] + if (y, x) in memo: + return memo[(y, x)] + m, n = int(x), int(y) + while n != 0: + t = m % n + m, n = n, t + memo[(x, y)] = m + return m + +# count number of possible permutation +# e.g., '1 1' => 12 +# e.g., '2 2 1' => 12233, 21233, 22133, 22313, 22331, 12323, 21323, 23123, 23213, 23231, 12332, 21332, 23132, 23312, 23321 +# e.g., '3' => 111(*2) +def count_permutation(s, memo_f, memo_p): + if s not in memo_p: + group = {} + length = 0 + for c in s.split(): + size = int(c) + if size in group: + group[size] += 1 + else: + group[size] = 1 + length += size + result = memo_f[length] + for size, freq in group.items(): + if freq not in memo_f: + memo_f[freq] = math.factorial(freq) + result /= memo_f[freq] * (size ** freq) + memo_p[s] = result + return memo_p[s] + + + + + +def _solution(w, h, s): + return str(sum([s**cycle for cycle in _generate_cycles(w, h)])/(math.factorial(w)*math.factorial(h))) + +def _generate_cycles(w, h): identity = np.arange(w*h).reshape((w, h)) - for i in generate_permutation(list(range(w))): - for j in generate_permutation(list(range(h))): - yield count_cycle(identity[i][:, j]) + for i in _generate_permutation(list(range(w))): + for j in _generate_permutation(list(range(h))): + #print i, j, _count_cycle(identity[i][:, j]) + yield _count_cycle(identity[i][:, j]) -def generate_permutation(numbers): +def _generate_permutation(numbers): if len(numbers) == 1: yield numbers else: for i, n in enumerate(numbers): - for j in generate_permutation(numbers[:i] + numbers[i+1:]): + for j in _generate_permutation(numbers[:i] + numbers[i+1:]): yield [n] + j -def count_cycle(arr): +def _count_cycle(arr): cycle = np.zeros(arr.shape, dtype=int) w, h = arr.shape cycle_id = 0 @@ -32,20 +115,33 @@ def count_cycle(arr): x, y = arr[x, y]/h, arr[x, y]%h return cycle_id -tests = [ - ([2, 3, 4], '430'), - ([2, 2, 2], '7'), - ([1, 1, 2], '2'), - ([1, 1, 3], '3'), - ([2, 1, 2], '3'), - ([1, 2, 2], '3'), - ([3, 1, 2], '4'), - ([1, 3, 2], '4'), - ([2, 3, 2], '13'), +tests = [ + #([2, 3, 4], '430'), + #([2, 2, 2], '7'), + #([1, 1, 2], '2'), + #([1, 1, 3], '3'), + #([2, 1, 2], '3'), + #([1, 2, 2], '3'), + #([3, 1, 2], '4'), + #([1, 3, 2], '4'), + #([4, 1, 2], '5'), + #([1, 4, 2], '5'), + #([5, 1, 2], '6'), + #([1, 5, 2], '6'), + #([6, 1, 2], '7'), + #([1, 6, 2], '7'), + ([12, 1, 2], '13'), + #([1, 12, 2], '13'), + #([2, 3, 2], '13'), + #([3, 3, 2], '36'), + #([4, 4, 2], '317'), + #([5, 4, 2], '1053'), + #([5, 5, 2], '5624'), + #([6, 5, 2], '28576'), + #([6, 6, 2], '251610'), + #([12, 12, 20], '251610'), ] for i, o in tests: result = solution(*i) print (i, result == o, result, o) - - -- 2.47.2 From 7d237786c0d93290cd4801e73725d281984ed773 Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Mon, 21 Mar 2022 16:13:55 +0900 Subject: [PATCH 5/6] Unroll large number division --- level5/disorderly-escape/solution.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/level5/disorderly-escape/solution.py b/level5/disorderly-escape/solution.py index 3c8b92e..b90c70a 100644 --- a/level5/disorderly-escape/solution.py +++ b/level5/disorderly-escape/solution.py @@ -9,7 +9,10 @@ def solution(w, h, s): if c not in memo_sp: memo_sp[c] = s ** c cases += memo_sp[c] - return str(cases / (memo_f[w] * memo_f[h])) + result = cases + result /= memo_f[w] + result /= memo_f[h] + return str(result) def generate_cycle_count(w, h, memo_f = {}): memo_c = {} @@ -75,7 +78,9 @@ def count_permutation(s, memo_f, memo_p): for size, freq in group.items(): if freq not in memo_f: memo_f[freq] = math.factorial(freq) - result /= memo_f[freq] * (size ** freq) + result /= memo_f[freq] + for i in range(freq): + result /= size memo_p[s] = result return memo_p[s] -- 2.47.2 From 7f1b481181c52cbcbce4957ad86ee234d0cc3e07 Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Mon, 21 Mar 2022 16:31:19 +0900 Subject: [PATCH 6/6] finish level5/disorderly-escape --- README.md | 5 ++ level5/disorderly-escape/solution.py | 101 ++++++++------------------- 2 files changed, 35 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 967e502..6cefd6f 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,11 @@ * Feedback > [level5/dodge-the-lasers] (1) Some of the text readme.txt are duplicated. (2) Though the input type is string, Java input example uses quotes for string instead of double quotation marks. (3) While expected output type is string, in the output example does not use quotes. So the readers can confuse the return type. +### disorderly-escape +* Completed in: 9 days, 17 hrs, 30 mins, 3 secs. +* Reference + * [Burnside's lemma: counting up to symmetries, Youtube](https://www.youtube.com/watch?v=D0d9bYZ_qDY) + ## Encrypted message CFcSBwgCCBoHRhkKU1cGAA4AGU5YQR5THBwNFwoGGAxTQQMQVBUSBg4EAAwQRhUQVBUHFAQTGRpT QQMQVBkPERkECQAWDVwXX1BGEwgJBAwCBFRVHQRGUlFBShwaDVZTGBUFVUdBShsVA1tZBwNGUlFB ShoVB1wXX1BGFAQOSklOQR5HGh5AVRY= diff --git a/level5/disorderly-escape/solution.py b/level5/disorderly-escape/solution.py index b90c70a..2e49686 100644 --- a/level5/disorderly-escape/solution.py +++ b/level5/disorderly-escape/solution.py @@ -5,14 +5,11 @@ def solution(w, h, s): memo_f = {w:math.factorial(w), h:math.factorial(h)} memo_sp = {} cases = 0 - for c in generate_cycle_count(w, h, memo_f): - if c not in memo_sp: - memo_sp[c] = s ** c - cases += memo_sp[c] - result = cases - result /= memo_f[w] - result /= memo_f[h] - return str(result) + for weight, n_cycles in generate_cycle_count(w, h, memo_f): + if n_cycles not in memo_sp: + memo_sp[n_cycles] = s ** n_cycles + cases += weight * memo_sp[n_cycles] + return str(cases / (memo_f[w] * memo_f[h])) def generate_cycle_count(w, h, memo_f = {}): memo_c = {} @@ -24,10 +21,8 @@ def generate_cycle_count(w, h, memo_f = {}): for i in pw.split(): for j in ph.split(): cycle_count += gcd(i, j, memo_gcd) - cases = count_permutation(pw, memo_f, memo_p) * count_permutation(ph, memo_f, memo_p) - #print pw, ph, cases, cycle_count - for i in range(cases): - yield cycle_count + variations = count_permutation(pw, memo_f, memo_p) * count_permutation(ph, memo_f, memo_p) + yield variations, cycle_count # generate cycle list in a string format # e.g., a string with length 5 can be composed with '2 2 1' which means there are 3 cycles, and foreach size is 2, 2 and 1. @@ -84,67 +79,31 @@ def count_permutation(s, memo_f, memo_p): memo_p[s] = result return memo_p[s] - - - - -def _solution(w, h, s): - return str(sum([s**cycle for cycle in _generate_cycles(w, h)])/(math.factorial(w)*math.factorial(h))) - -def _generate_cycles(w, h): - identity = np.arange(w*h).reshape((w, h)) - for i in _generate_permutation(list(range(w))): - for j in _generate_permutation(list(range(h))): - #print i, j, _count_cycle(identity[i][:, j]) - yield _count_cycle(identity[i][:, j]) - -def _generate_permutation(numbers): - if len(numbers) == 1: - yield numbers - else: - for i, n in enumerate(numbers): - for j in _generate_permutation(numbers[:i] + numbers[i+1:]): - yield [n] + j - -def _count_cycle(arr): - cycle = np.zeros(arr.shape, dtype=int) - w, h = arr.shape - cycle_id = 0 - for i in range(w): - for j in range(h): - x, y = i, j - if cycle[x, y] == 0: - cycle_id += 1 - while cycle[x, y] == 0: - cycle[x, y] = cycle_id - x, y = arr[x, y]/h, arr[x, y]%h - return cycle_id - tests = [ - #([2, 3, 4], '430'), - #([2, 2, 2], '7'), - #([1, 1, 2], '2'), - #([1, 1, 3], '3'), - #([2, 1, 2], '3'), - #([1, 2, 2], '3'), - #([3, 1, 2], '4'), - #([1, 3, 2], '4'), - #([4, 1, 2], '5'), - #([1, 4, 2], '5'), - #([5, 1, 2], '6'), - #([1, 5, 2], '6'), - #([6, 1, 2], '7'), - #([1, 6, 2], '7'), + ([2, 3, 4], '430'), + ([2, 2, 2], '7'), + ([1, 1, 2], '2'), + ([1, 1, 3], '3'), + ([2, 1, 2], '3'), + ([1, 2, 2], '3'), + ([3, 1, 2], '4'), + ([1, 3, 2], '4'), + ([4, 1, 2], '5'), + ([1, 4, 2], '5'), + ([5, 1, 2], '6'), + ([1, 5, 2], '6'), + ([6, 1, 2], '7'), + ([1, 6, 2], '7'), ([12, 1, 2], '13'), - #([1, 12, 2], '13'), - #([2, 3, 2], '13'), - #([3, 3, 2], '36'), - #([4, 4, 2], '317'), - #([5, 4, 2], '1053'), - #([5, 5, 2], '5624'), - #([6, 5, 2], '28576'), - #([6, 6, 2], '251610'), - #([12, 12, 20], '251610'), + ([1, 12, 2], '13'), + ([2, 3, 2], '13'), + ([3, 3, 2], '36'), + ([4, 4, 2], '317'), + ([5, 4, 2], '1053'), + ([5, 5, 2], '5624'), + ([6, 5, 2], '28576'), + ([6, 6, 2], '251610'), + ([12, 12, 20], '97195340925396730736950973830781340249131679073592360856141700148734207997877978005419735822878768821088343977969209139721682171487959967012286474628978470487193051591840'), ] for i, o in tests: -- 2.47.2