问题:给定若干个整数opnum[1] opnum[2] ... opnum[N-1]作为操作数,按如下方式进行运算
得到一个期望结果值desired_result:
opnum[0] op[0] opnum[1] ... op[N-2] opnum[N-1] = desired_result
其中op[i]仅限于加减乘除,而且不使用括号。
在给定opnum[i]和desired_result的条件下,试求出op[i]的可能组合。
每种操作符号使用次数不限。假设运算仅限于整数域中。
分析:
1. 基本算法
由于要给出所有可能组合,所以最基本的方法就是穷举法。如果输入操作数为N(>=2)个,则操作符号
为N-1个,共有pow(4,N-1)种组合。
2. 运算优先度
由于乘和除的运算优先度高于加和减,所以对于每一种可能的运算表达式(共有pow(4,N-1)种组合),
不能单纯从左向右进行运算。要优先进行乘法和除法运算。所以在每次从左向右扫描时,表达式中的加法
或减法运算符只有当其右边不是乘或者除,或者本身已经是最后一个运算符时才应该被执行。
每个运算符都被执行后,最后比较运算结果是否与期望结果吻合以判断该运算组合是否为一个解。
3. 运算数据和操作符的存储
3-1 在初始状态,运算数据存储在N个单元(e.g, sequence or list)中,N-1个操作符存储在
N-1个单元中。
3-2 每个运算会导致运算数据减少一个,运算操作符也减少一个。可以考虑每执行一次运算即调整一次
操作数和操作运算符的存储调整,调整的方式是将后面的操作数和操作符往前挪一格,使得操作数和操
作符总是连续地排在前面几个存储单元中。基于这种存储及调节方式,可以知道第k个运算符相关联的
左操作数和右操作数分别是第k个操作数和第k+1个操作数。
1 import random 2 3 opsym = ['+','-','*','/'] # Only for final result logging. 4 5 ##在一定范围内随机生成若干个操作数以及期望结果(也可改为在运行时以交互方式输入,与后面的算法 6 ##实现相对独立) 7 numOfOpnum = 5 #random.randrange(2,5); # 暂时考虑固定5个操作数. 8 9 opnum = [] 10 11 for i in range(numOfOpnum): 12 opnum.append(random.randrange(1,20)) 13 14 #-保留原始备份,因为opnum在以下处理会被改变。But "opnum_bak=opnum" doesn't work. Do you know why? 15 opnum_bak = opnum.copy(); 16 17 desiredRslt = random.randrange(1,20) 18 19 for i in range(4**(numOfOpnum-1)): 20 opnum = opnum_bak.copy() 21 # 生成第i种运算操作符组合,分别以0,1,2,3代表加减乘除. 22 op = [] 23 quotient = i; 24 for j in range(numOfOpnum-1): 25 op.append(quotient%4); 26 quotient = quotient//4; #‘//’表示整数除法 27 28 op_bak = op.copy(); 29 30 # 进入循环处理直到最后只剩下1个操作数(或者0个操作符)为止,最后剩下的操作数就是运算结果。 31 while len(op) > 0: 32 # 按顺序遍历剩下的运算符,符合条件即进行相应运算。每次循环只做碰到的第一次合法运算, 33 # 运算完毕重新调整opnum和op(分别删除一个操作数和一个操作符),然后退回到上一级循环。 34 # 但是list对象没有从中间任意位置删除某一项的method!怎么办呢?slice+concatenation! 35 for j in range(len(op)): 36 if j == (len(op)-1): 37 if op[j] == 0: 38 opnum[j] = opnum[j] + opnum[j+1]; #执行运算并将结果存回opnum[j]. 39 opnum.pop(); #这种情况下已知是删除末尾的所以可以用pop()。下同。 40 op.pop(); 41 break 42 elif op[j]==1: 43 opnum[j] = opnum[j] - opnum[j+1]; 44 opnum.pop(); 45 op.pop(); 46 break 47 elif op[j]==2: 48 opnum[j] = opnum[j] * opnum[j+1]; 49 opnum.pop(); 50 op.pop(); 51 break 52 else: #op[j]==3 53 opnum[j] = opnum[j] // opnum[j+1]; #整除! 54 opnum.pop(); 55 op.pop(); 56 break 57 else: 58 if op[j] == 2: 59 opnum[j] = opnum[j] * opnum[j+1]; 60 opnum = opnum[0:j+1]+opnum[j+2:]; # remove opnum[j+1]. 61 op = op[0:j]+op[j+1:]; # remove op[j]. 62 break 63 elif op[j] == 3: 64 opnum[j] = opnum[j] // opnum[j+1]; 65 opnum = opnum[0:j+1]+opnum[j+2:]; # remove opnum[j+1]. 66 op = op[0:j]+op[j+1:]; # remove op[j]. 67 break 68 elif op[j] == 0 and op[j+1]<2: #后面跟的不是乘或除时才执行对应运算。 69 opnum[j] = opnum[j] + opnum[j+1]; 70 opnum = opnum[0:j+1]+opnum[j+2:]; # remove opnum[j+1]. 71 op = op[0:j]+op[j+1:]; # remove op[j]. 72 break 73 elif op[j] == 1 and op[j+1]<2: #后面跟的不是乘或除时才执行对应运算。 74 opnum[j] = opnum[j] - opnum[j+1]; 75 opnum = opnum[0:j+1]+opnum[j+2:]; # remove opnum[j+1]. 76 op = op[0:j]+op[j+1:]; # remove op[j]. 77 break 78 else: 79 pass 80 81 82 # 打印当前运算符号组合的结果 83 if opnum[0] == desiredRslt: 84 print(opnum_bak[0],opsym[op_bak[0]],opnum_bak[1],opsym[op_bak[1]],opnum_bak[2],opsym[op_bak[2]],opnum_bak[3],opsym[op_bak[3]],opnum_bak[4],'=',desiredRslt,':OK' ); 85 else: 86 print(opnum_bak[0],opsym[op_bak[0]],opnum_bak[1],opsym[op_bak[1]],opnum_bak[2],opsym[op_bak[2]],opnum_bak[3],opsym[op_bak[3]],opnum_bak[4],'!=',desiredRslt,':NG' );