def expression(target_number): """ Given an integer n as input, will produce a mathematical expression whose solution is n :param target_number: int, n :return: string, the mathematical expression or the string 'no solution' """ # Generate possible operations layouts as strings operations = combine('+-/*', 3) # For every combination layout for combination in operations: # Generate every permutation of the current combination operations_layouts = permutations(combination) # For every permutation layout for operation in operations_layouts: # Create the mathematical expression equation = '4 {} 4 {} 4 {} 4 == {}'.format(operation[0], operation[1], operation[2], target_number) # Evaluate the expression correct = eval(equation) # If true if correct: return equation.replace('==', '=') # Otherwise return 'no solution'
def generate_vampires(digits, fangs): fang_digits = digits // fangs fang_start = 10 ** (fang_digits - 1) fang_stop = 10 ** fang_digits for fangs in combine(range(fang_start, fang_stop), fangs): vampire_candidate = reduce(operator.mul, fangs, 1) vampire_candidate_digits = sorted(list(str(vampire_candidate))) if len(vampire_candidate_digits) != digits: continue expected_digits = sorted(list(''.join(str(fang) for fang in fangs))) if expected_digits == vampire_candidate_digits: yield vampire_candidate, fangs
def named_edges(self): """ Return a dictionary of named edges. """ named = {} strings = list(string.ascii_lowercase) key_len = (len(self.edges) // len(strings) + 1) + 1 keys = strings + list(combine(strings, key_len)) i = 0 for edge in self.edges: key = ''.join(keys[i]) named[key] = edge i += 1 return named
def _vertex_outputs(self, number_of_outputs: int = 2) -> list: """ get the outputs from a vertex subject to: * no trivial vertices: output != input, * keep invariants invariant, * return required number of particles. """ particle_instances = [x() for x in particle.PARTICLES] tuples = combine(particle_instances, number_of_outputs) allowed_tuples = [] for t in tuples: try: self._legal_vertex(t) allowed_tuples.append(t) except InteractionError: pass if allowed_tuples: return allowed_tuples raise InteractionError("{} can't generate {} legal outputs".format( [t for t in tuples], number_of_outputs))
def main(): with open("input", "r") as f: nums = list(map(int, f.read().split())) print(prod(next(i for i in combine(nums, 2) if sum(i) == 2020))) print(prod(next(i for i in combine(nums, 3) if sum(i) == 2020)))
def factorise_further(factor_tuple, expand=False, rearrangements=True): """ Given an initial factorisation, `factor_tuple`, find all pairs of factors returned by `factorise(n)` for each factor `n` in `factor_tuple`. Note: this function is to be passed one tuple representing a factorisation, not a list of tuples representing multiple possible factorisations! Having enumerated the pairs of factors, either there will be only the trivial factorisation of {1,n} (i.e. n is prime) or `n` will admit further factorisations. If a factor `n` can be factored further, then enumerate all of these factors as a list (that list may then be factored further, and so on). This function will return the lists of further factorisations for each of the integers given in the tuple `factor_tuple`. E.g. given (1,1) ==> [[1], [1]] (1,2) ==> [[1], [2]] (1,4) ==> [[1], [4, (2,2)]] (4,4) ==> [[4, (2,2)], [4, (2,2)]] (4,5) ==> [[4, (2,2)], [5]] ... and so on. If `expand` is True (default: False), these will be expanded out to the list of all possible combinations (i.e. all possible factorisations) using the `itertools.product` function (which is imported under the alias `combine`). E.g. given (1,1) ==> [(1, 1)] (1,2) ==> [(1, 2)] (1,4) ==> [(1, 4), (1, 2, 2))] (4,4) ==> [(4, 4), (4, 2, 2), (2, 2, 4), (2, 2, 2, 2)] (4,5) ==> [(4, 5), (2, 2, 5)] ... If `rearrangements` is False (default: True), rearrangements will not removed from the results. In the previous example, shown for `rearrangements` as True, for example the line for `(4,4)` contains both `(4,2,2)` and `(2,2,4)`, arising by splitting the first 4 and the second 4 into `(2,2)` before flattening the list. Below are the same examples for `rearrangements` set to False. E.g. given (1,1) ==> [(1, 1)] (1,2) ==> [(1, 2)] (1,4) ==> [(1, 4), (1, 2, 2)] (4,4) ==> [(4, 4), (4, 2, 2), (2, 2, 2, 2)] (4,5) ==> [(4, 5), (2, 2, 5)] ... Note that you can get infinitely many factorisations if you permit 1 to be factored into (1,1) i.e. [1, (1,1), (1,1,1), (1,1,1,1), ...] so primes are not factored into the trivial factorisation and just kept as single integers. The lists returned by this function will be of the same length as the input tuple, and will always have as their first entry integer at the corresponding index of this input tuple (i.e. it will return a list of only this integer, n, or a list beginning with the integer, n, followed by tuples representing factorisations of that integer, n). """ complete_factorisations = [] for n_in_tuple, factor in enumerate(factor_tuple): extra_factorisations = [ factor ] # extra factorisations will be appended later initial_factorisations = factorise(factor, nontrivial_only=True) # This loop will be skipped if `factor` admits only the trivial factorisation: for factorisation in initial_factorisations: extra_factorisations.append(factorisation) complete_factorisations.append(extra_factorisations) if expand: all_factorisations = list(combine(*complete_factorisations)) all_factorisations_flattened = [] for comb in all_factorisations: flat = chain.from_iterable([[a] if type(a) == int else a for a in comb]) all_factorisations_flattened.append(tuple(flat)) if rearrangements: return all_factorisations_flattened else: without_rearrangements = dedup_list(all_factorisations_flattened) return without_rearrangements else: return complete_factorisations