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)
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)