def factor2(target):

    import multiprocessing
    if 1:
        pool = multiprocessing.Pool(10)
    else:
        pool = None

    print factorisation_summary(target)

    start_time = time()
    num_states = 0

    if not isinstance(target, IntegerBaseFixed):
        target = IntegerBaseFixed(target, None, None)

    solved = False


#### SET HEURISTIC

#    key_func = partial(product_to_dual_error, target=target, comb_func=min)
    key_func = partial(product_to_simple_error, target=target)
#    key_func = partial(product_to_hamming_distance_padded, target=target)
    frontiers = defaultdict(lambda : BinarySearchTree(key_func))

####


    # Find the maximum number of digits
    max_factor_num_digits = target_to_max_factor_num_digits(target, 0)

    start_nodes = []

#    init_p_digits = [0 for _ in xrange(max_factor_num_digits)]
#    init_q_digits = [0 for _ in xrange(max_factor_num_digits)]

    import math
    sqrt = math.sqrt(target.as_int)
    init_p_digits = map(int, int_to_digits(int(sqrt)))
    init_q_digits = init_p_digits[:]
    assert len(init_p_digits) == max_factor_num_digits
    print init_p_digits

    for p0, q0 in itertools.combinations_with_replacement(xrange(BASE), 2):
        for pn, qn in itertools.combinations_with_replacement(xrange(1, BASE), 2):

            p_digits = init_p_digits[:]
            p_digits[0] = p0
            p_digits[-1] = pn
            p = IntegerBaseFixed(p_digits, 1, 1)

            q_digits = init_q_digits[:]
            q_digits[0] = q0
            q_digits[-1] = qn
            q = IntegerBaseFixed(q_digits, 1, 1)

            prod = ProductFixed(p, q)
            start_nodes.append(prod)
            num_states += 1

    # Do some preprocessing to get in the right range immediately by fixing the
    # larger powers
    start_nodes = filter_correct_digits_left(start_nodes, target)
#    for _ in start_nodes:
#        print _
#    return
    while 0 < len(start_nodes) < 10:
        print len(start_nodes)
        start_nodes = list(itertools.chain(*[node.generate_children_right() for node in start_nodes]))
        start_nodes = filter_digit_length(start_nodes, min(max_factor_num_digits, target.num_digits))
        start_nodes = [node for node in start_nodes if node.as_int <= target.as_int]
        start_nodes = filter_small_products(start_nodes, target)

    print len(start_nodes)
    return

    for prod in start_nodes:
        frontiers[prod.num_fixed_digits].insert(prod)

    _iter = 0
    while sum(map(len, frontiers.itervalues())):
    #for _iter in xrange(1000):

        _iter += 1
        # Now queue the stuff we want to expand
        # First work out how many we want
### Budget
        budget = 1000
        dig_to_num_instances = {dig: len(tree) for dig, tree in frontiers.iteritems()}

        # Filter out zero instances
#        dig_to_num_instances = {dig: num for dig, num in dig_to_num_instances.iteritems() if num != 0}
        # And instances with too many digits
#        dig_to_num_instances = {dig: num for dig, num in dig_to_num_instances.iteritems() if dig < max_factor_num_digits}

        total_inst = sum(dig_to_num_instances.values())
        if total_inst > budget:

            # Blind sweeper
#            dig_to_expand = sorted(dig_to_num_instances.keys())[_iter % len(dig_to_num_instances)]
#            dig_to_num_instances = {}
#            dig_to_num_instances[dig_to_expand] = budget

#            dig_to_num_instances = alloc_by_exp_weight(dig_to_num_instances, budget)
            dig_to_num_instances = alloc_flat(dig_to_num_instances, 1)
#            dig_to_num_instances = alloc_exp_interp(dig_to_num_instances, 1, 5)
#            dig_to_num_instances = alloc_exp_increasing(dig_to_num_instances, 1.3)
            # Shitty measure that's relative to how many instances there are of each digit. Prone to getting stuck
            #instances_per_iter = min(total_inst, desired_instances_to_expand)
            #dig_to_num_instances = {dig: int(num * instances_per_iter / total_inst) for dig, num in dig_to_num_instances.iteritems()}


        # Do some normalisation
        dig_to_num_instances = {dig: min(max(num, MIN_TO_EXPAND), len(frontiers[dig])) for dig, num in dig_to_num_instances.iteritems()}



######### Printing

        # Print out how we are allocating the budget
        if not _iter % 100 or 1:
#            sleep(0.2)
            print 'Num instances checked:', num_states
            print 'Solution location:'
            find_solution_root_fixed(frontiers, target)
#
#            keys = sorted(frontiers.keys())
#            print 'Budget Allocation:'
#            for dig in keys[-5:]:
#                num_inst = len(frontiers[dig])
#                if num_inst == 0:
#                    continue
#                print dig, dig_to_num_instances.get(dig, 0), num_inst
            print
        #print dig_to_num_instances
        #print {dig: len(tree) for dig, tree in frontiers.iteritems()}
        #print

#########





        # Now get them
        to_expand = []
        for num_dig, num_inst in dig_to_num_instances.iteritems():
            frontier = frontiers[num_dig]
            for _ in xrange(num_inst):
                to_expand.append(frontier.pop_min())
#                if len(frontier):
#                    to_expand.append(frontier.pop_min())
#                if len(frontier):
#                    to_expand.append(pop_index(frontier, key_func, int(len(frontier) / 2)))


### Old linear implementation
#        for candidate in to_expand:
#            #print
#            #print IntegerBaseFixed.from_int(target)
#            #print candidate
#            #from time import sleep
#            #sleep(1)
#
#            if candidate.num_fixed_digits_left <= candidate.num_fixed_digits_right:
#                children = candidate.generate_children_left()
#            else:
#                children = candidate.generate_children_right()
#
#            children = filter_digit_length(children, min(max_factor_num_digits, target.num_digits))
#            children = [child for child in children if child.as_int <= target.as_int]
##            children = [child for child in children if child.num_digits <= target.num_digits]
#            children = filter_correct_digits_left(children, target)
#            children = filter_small_products(children, target)
#
#            for child in children:
#                if child.as_int == target.as_int:
#                    print 'Solution!', child
#                    solved = True
#                    break
#
#                frontiers[child.num_fixed_digits].insert(child)
#
#            num_states += len(children)


### New parallelisable approach
        mapper = partial(expand_and_filter_children, target=target,
                         max_factor_num_digits=max_factor_num_digits)

        if pool is None:
            children = map(mapper, to_expand)
        else:
            children = pool.map(mapper, to_expand)

        children = itertools.chain(*children)
        for child in children:
            num_states += 1
            if child.as_int == target.as_int:
                print 'Solution!', child
                solved = True
                break

            frontiers[child.num_fixed_digits].insert(child)

        #print 'Checked:', num_states
        if solved:
            break

    end_time = time()
    assert solved
    print 'Iterations:', _iter
    print 'States checked:', num_states
    print 'Time taken: {:.2f}s'.format(end_time - start_time)
    return num_states, round(end_time - start_time, 2)
示例#2
0
def factor2(target):
    start_time = time()
    num_states = 0

    if not isinstance(target, IntegerBase2):
        target = IntegerBase2(target)

    solved = False
    key_func = partial(lambda x, target: target.as_int - x.as_int, target=target)
    frontiers = defaultdict(lambda : BinarySearchTree(key_func))

    num_digits = target_to_max_factor_num_digits(target, 0)
    for i, j in itertools.combinations_with_replacement(xrange(BASE), 2):
        p_digits = [0 for _ in xrange(num_digits)]
        q_digits = [0 for _ in xrange(num_digits)]
        p_digits[-1] = i
        q_digits[-1] = j
        _prod = Product(p_digits, q_digits)
        frontiers[_prod.num_fixed_digits].insert(_prod)

    _iter = 0
    while sum(map(len, frontiers.itervalues())):
    #for _iter in xrange(1000):
        _iter += 1

        find_solution_root(frontiers, target)

        # Now queue the stuff we want to expand
        # First work out how many we want
        budget = 10000
        dig_to_num_instances = {dig: len(tree) for dig, tree in frontiers.iteritems()}
        total_inst = sum(dig_to_num_instances.values())
        if total_inst > budget:
#            dig_to_num_instances = alloc_by_exp_weight(dig_to_num_instances, budget, lambda_=0.25)
            dig_to_num_instances = alloc_flat(dig_to_num_instances, 10)
#            dig_to_num_instances = alloc_exp_interp(dig_to_num_instances, 2, 10)
            # Shitty measure that's relative to how many instances there are of each digit. Prone to getting stuck
            #instances_per_iter = min(total_inst, desired_instances_to_expand)
            #dig_to_num_instances = {dig: int(num * instances_per_iter / total_inst) for dig, num in dig_to_num_instances.iteritems()}


        # Do some normalisation
        dig_to_num_instances = {dig: min(max(num, MIN_TO_EXPAND), len(frontiers[dig])) for dig, num in dig_to_num_instances.iteritems()}

        # Print out how we are allocating the budget
        if 0:
            for dig in sorted(dig_to_num_instances.keys()):
                if dig_to_num_instances[dig] == 0:
                    continue
                print dig, dig_to_num_instances[dig], len(frontiers[dig])
            print
        #print dig_to_num_instances
        #print {dig: len(tree) for dig, tree in frontiers.iteritems()}
        #print

        # Now get them
        to_expand = []
        for num_dig, num_inst in dig_to_num_instances.iteritems():
            frontier = frontiers[num_dig]
            for _ in xrange(num_inst):
                to_expand.append(frontier.pop_min())

        for candidate in to_expand:
            #print
            #print IntegerBase2.from_int(target)
            #print candidate
            #from time import sleep
            #sleep(1)

            children = candidate.generate_children()
#            children = filter_digit_length(children, target)
#            children = filter_correct_digits(children, target)
            children = [child for child in children if child.as_int <= target.as_int]
            children = filter_small_products(children, target)

            for child in children:
                if child.as_int == target:
                    print 'Solution!', child
                    solved = True
                    break

                frontiers[child.num_fixed_digits].insert(child)

            num_states += len(children)

        #print 'Checked:', num_states
        if solved:
            break

    end_time = time()
    print 'Iterations:', _iter
    print 'States checked:', num_states
    print 'Time taken: {:.2f}s'.format(end_time - start_time)