Skip to content

Instantly share code, notes, and snippets.

@vyach-vasiliev
Last active April 8, 2017 12:08
Show Gist options
  • Save vyach-vasiliev/6b6ce4616b3f518fceea17f671ab9f00 to your computer and use it in GitHub Desktop.
Save vyach-vasiliev/6b6ce4616b3f518fceea17f671ab9f00 to your computer and use it in GitHub Desktop.
Get combinations of expressions for number N
import re, json, collections, math, itertools
def get_comb_exp_for_number_v32_clean(for_number, numbers, wo_dupl=False, more=False, debug=False):
""" v3.2 Clean version
wo_dupl - Returns right expressions without duplicates (9.*2./1.-8. = 2.*9./1.-8.)
more - Returns detailed statistics
debug - Returns right expressions and print detailed statistics
"""
def iter_expressions(numbers, operations):
''' Iterator. Formating numbers and operations to expression: [1,2,3,4] and [+,-,/] -> 1.+2.-3./4. '''
for n in numbers:
for o in operations:
yield '{x[0]}.{y[0]}{x[1]}.{y[1]}{x[2]}.{y[2]}{x[3]}.'.format(x=n, y=o)
def get_expressions(numbers, operations):
''' Formating numbers and operations to expression: [1,2,3,4] and [+,-,/] -> 1.+2.-3./4. '''
return ['{x[0]}.{y[0]}{x[1]}.{y[1]}{x[2]}.{y[2]}{x[3]}.'.format(x=n, y=o) for n in numbers for o in operations]
def format_expression(exp):
''' Adding round brackets: 2.*9.-1.*8. -> (2.*9.-1.)*8., 8.+2.*1.-9. -> (8.+2.)*1.-9. '''
for_rb_re = re.compile(r'(((\d\.)[*/])?(\d\.[+-]){1,}\d\.(?=[*/]))')
if for_rb_re.match(exp):
return for_rb_re.sub(r'(\1)', exp)
return exp
def clear_duplicates(exp_arr):
dupl_arr_re = [
re.compile(r'(?<![\*/-])(\d\.[+]){1,}\d\.'),
re.compile(r'(?<![\*/+])(\d\.[-]){1,}\d\.'),
re.compile(r'(?<![+-/])((\d\.\/1\.|\d\.)[\*]){1,}\d\.'),
re.compile(r'(?<![+\-\*])(((?<=/)1\.|[2-9]\.)[/]){1,}\d\.'),
]
msk_same_type = re.compile('([023456789]|(?<!(\*|/))1)')
exp_remove_arr = []
pseudo_good_arr = [] # Here not all positive expressions, but only a part
for exp in exp_arr:
if exp in exp_remove_arr:
continue
for item_re in dupl_arr_re:
_same_type = item_re.search(exp)
if _same_type:
same_type = _same_type.group(0)
numbers_str = ''.join([x[0] for x in msk_same_type.findall(same_type)])
if len(set(numbers_str)) <= 1: # '22', '2.-2.+3.-3.', '{}.-{}.+3.-3.'
continue
exp_mask = msk_same_type.sub(r"{}", same_type)
exp_mask = exp[:_same_type.start(0)] + exp_mask + exp[_same_type.start(0) + len(same_type):] # '(2.*9.-8.)*1.', '2.*9.', '{}.*{}.' -> '({}.*{}.-8.)*1.'
numbers_permut = list(set(itertools.permutations(numbers_str, len(numbers_str))))
for i, item in enumerate(numbers_permut):
if exp in exp_remove_arr:
continue
if i == 0:
w = exp_mask.format(*list(item))
pseudo_good_arr.append(w)
if i > 0: # here must be check of user mask (+-+)
w = exp_mask.format(*list(item))
if len(set(numbers_str)) <= 2 and not w in pseudo_good_arr:
exp_remove_arr.append(w)
elif len(set(numbers_str)) > 2:
exp_remove_arr.append(w)
exp_arr = set(exp_arr) - set(exp_remove_arr) # remove b from a
return exp_arr
if isinstance(numbers, list) or isinstance(numbers, tuple): numbers = ''.join(numbers)
right_expressions_arr = []
numbers_arr = list(itertools.permutations(numbers, 4))
operations_arr = list(itertools.product('+-/*', repeat=3))
expressions_arr = list(set(get_expressions(numbers_arr, operations_arr)))
expressions_arr_format = [format_expression(exp) for exp in expressions_arr]
if wo_dupl and len(list(set(numbers))) > 1: expressions_arr_format = clear_duplicates(expressions_arr_format)
for exp in expressions_arr_format:
nbf = eval(exp, {'__builtins__': None}) # calculating
if float(for_number) == float(nbf):
right_expressions_arr.append(exp)
all_expressions_count = len(expressions_arr)
right_expressions = list(set(right_expressions_arr))
right_comb_numbers = [re.sub("\D", "", i) for i in set(right_expressions_arr)] # 1234
right_comb_operations = [re.sub("\w\.", "", i) for i in set(right_expressions_arr)] # +-*/
more_result = {
'all_expressions': all_expressions_count,
'right_expressions': right_expressions,
'right_comb_numbers': right_comb_numbers,
'right_comb_operations': right_comb_operations,
'right_expression_count': len(right_expressions),
'wrond_expressions_count': all_expressions_count - len(right_expressions),
}
if debug:
print(json.dumps(more_result, indent=2))
if not more:
return right_expressions
else:
return more_result
# if __name__ == '__main__':
# a = '1294' # 21 exps wo=False, 12 exps wo=True
# a = '3211' # 5exps wo=False, 2 exps wo=True
a = '1829' # (5, ['1.*2.*9.-8.', '2./1.*9.-8.', '2.*9./1.-8.', '(2.*9.-8.)/1.', '(2.*9.-8.)*1.'])
# a = '2829' # nothing
# a = '2233' # (3, ['2.*2.+3.+3.', '(3.+3.)*2.-2.', '3.+2.+3.+2.'])
# a = '5555' # (7, ['5.+5.+5.-5.', '(5.+5.)*5./5.', '(5.+5.)/5.*5.', '5./5.*5.+5.', '5.+5.-5.+5.', '5.-5.+5.+5.', '5.*5./5.+5.'])
a = '2222' # (2, ['(2.+2.)*2.+2.', '2.*2.*2.+2.'])
#res = get_comb_exp_for_number_v32_clean(10, a, wo_dupl=False, debug=False)
res = get_comb_exp_for_number_v32_clean(10, a, wo_dupl=True, debug=False)
print('Goted operations: {}\n{}'.format(len(res), res))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment