Exemple #1
0
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'
Exemple #2
0
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
Exemple #3
0
    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
Exemple #4
0
    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)))
Exemple #6
0
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