Last active
July 29, 2019 14:20
-
-
Save olisolomons/85dc6fa6bf438d5461d12da16e38f7f2 to your computer and use it in GitHub Desktop.
Table scanner - answer to interview question
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Table: | |
| """A table of numbers""" | |
| def __init__(self, *args, **kwargs): | |
| if len(args) == 3: | |
| self.from_vals(*args) | |
| elif len(args) == 2: | |
| x, y = args | |
| if kwargs['mode'] == 'random': | |
| vals = [random.randint(0, 9) for _ in range(x * y)] | |
| self.from_vals(x, y, vals) | |
| elif kwargs['mode'] == 'blank': | |
| vals = [0 for _ in range(x * y)] | |
| self.from_vals(x, y, vals) | |
| def from_vals(self, x, y, vals): | |
| """Construct table from 1-dimensional array""" | |
| self.vals = [[vals[_x + _y * x] for _y in range(y)] for _x in range(x)] | |
| self.x = x | |
| self.y = y | |
| def __str__(self): | |
| """Print as formatted table""" | |
| def _str(item): | |
| if item is None: | |
| return '■' | |
| else: | |
| return str(item) | |
| max_width = max( | |
| len(_str(self[x, y])) | |
| for y in range(self.y) | |
| for x in range(self.x) | |
| ) | |
| def row_to_str_iter(y): | |
| for x in range(self.x): | |
| str_item = _str(self[x, y]) | |
| padding = " " * (max_width - len(str_item)) | |
| yield padding + str_item | |
| res = "" | |
| for y in range(self.y): | |
| res += ' | '.join(row_to_str_iter(y)) | |
| res += "\n" | |
| return res | |
| def __getitem__(self, item): | |
| """Allows for read access using `self[x, y]`""" | |
| x, y = item | |
| return self.vals[x][y] | |
| def __setitem__(self, key, value): | |
| """Allows for write access using `self[x, y] = value`""" | |
| x, y = key | |
| self.vals[x][y] = value | |
| def rows(self): | |
| """Iterate through rows""" | |
| for y in range(self.y): | |
| yield [self[x, y] for x in range(self.x)] | |
| def columns(self): | |
| """Iterate through columns""" | |
| for x in range(self.x): | |
| yield [self[x, y] for y in range(self.y)] | |
| def copy(self): | |
| """Deep copy""" | |
| vals = [self[x, y] for y in range(self.y) for x in range(self.x)] | |
| return Table(self.x, self.y, vals) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import itertools | |
| import random | |
| from table import Table | |
| def right_sum_rows(table): | |
| """Sum rows from left to right. Each result cell is the sum of all input cells to the right.""" | |
| sum_table = [] | |
| for row in table.rows(): | |
| sum = 0 | |
| for x in range(table.x): | |
| sum_table.append(sum) | |
| sum += row[x] | |
| return Table(table.x, table.y, sum_table) | |
| def find_row_candidates(table, right_sum): | |
| """Sum rows right to left, to find all row candidates. Returns a bitset of candidates.""" | |
| candidates = 0 | |
| for y, (row, row_sum) in enumerate(zip(table.rows(), right_sum.rows())): | |
| sum = 0 | |
| for x in reversed(range(table.x)): | |
| if sum == row_sum[x]: | |
| candidates |= 1 << (x + y * table.x) | |
| sum += row[x] | |
| return candidates | |
| def down_sum_columns(table): | |
| """Sum columns from top to bottom. Each result cell is the sum of all input cells above it.""" | |
| sum_table = Table(table.x, table.y, mode='blank') | |
| for x, column in enumerate(table.columns()): | |
| sum = 0 | |
| for y, item in enumerate(column): | |
| sum_table[x, y] = sum | |
| sum += item | |
| return sum_table | |
| def find_column_candidates(table, down_sum): | |
| """Sum rows bottom to top, to find all column candidates. Returns a bitset of candidates.""" | |
| candidates = 0 | |
| for x, (column, column_sum) in enumerate(zip(table.columns(), down_sum.columns())): | |
| sum = 0 | |
| for y in reversed(range(table.y)): | |
| if sum == column_sum[y]: | |
| candidates |= 1 << (x + y * table.x) | |
| sum += column[y] | |
| return candidates | |
| # table = Table(5, 5, mode='blank') | |
| table = Table(6, 5, | |
| [0, 6, 1, 0, 0, 0, | |
| 0, 0, 5, 0, 0, 0, | |
| 0, 1, 0, 0, 0, 0, | |
| 9, 5, 0, 0, 8, 6, | |
| 0, 1, 0, 0, 0, 0] | |
| ) | |
| print('Table') | |
| print(table) | |
| right_sum = right_sum_rows(table) | |
| print('Left to right sum') | |
| print(right_sum) | |
| candidates = find_row_candidates(table, right_sum) | |
| def print_candidates(candidates): | |
| candidates_table = table.copy() | |
| i = 0 | |
| for y in range(table.y): | |
| for x in range(table.x): | |
| if (candidates >> i) & 1 == 1: | |
| candidates_table[x, y] = None | |
| i += 1 | |
| print('Candidates') | |
| print(candidates_table) | |
| print('Row candidates') | |
| print_candidates(candidates) | |
| down_sum = down_sum_columns(table) | |
| print('Top to bottom sum') | |
| print(down_sum) | |
| print('Column candidates') | |
| print_candidates(find_column_candidates(table, down_sum)) | |
| candidates &= find_column_candidates(table, down_sum) | |
| print('Final answer') | |
| print_candidates(candidates) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment