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)
#print IntegerBase.from_int(target) if __name__ == '__main__': EXP1 = 12709189 EXP20 = 455937533473 EXP30 = 292951160076082381 EXP40 = 1208925727750433490141601 EXP50 = 1267650589799944054235347335521 EXP70 = 696898287104567707994557936377097402581467 EXP100 = 803433889244035421575180038607945042433021719029044123614381 RSA100 = 1522605027922533360535618378132637429718068114961380688657908494580122963258952897654000350692006139 #target = 143 #target = 56153 target = EXP30 print factorisation_summary(target) analyse(target, product_to_hamming_distance, 10000000) raise Exception n = 17 nums = range(2**n, 2**(n+1)-1) nums = (num for num in nums if is_prime(num)) for p, q in itertools.combinations_with_replacement(nums, 2): import random if random.randint(0, 1) == 1: continue target = p*q