(原题出自微软公司面试题)问题如下:有两个序列a,b,大小都为n,序列元素的值任意整数,无序;要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。例如:a=[100,99,98,1,2, 3]b=[1, 2, 3, 4,5,40] 题目是看到QQ群友发的,网上也百度了下目前已经有好几种解法了。写了半天有点晕,后面忽然想到中学时候数学里面的排列组合的方法。方法对于较短的list可行,长list组合情况太多,可能耗时太长或溢出。
1 from itertools import combinations 2 3 def get_combinations(arr): 4 """获取所有组合结果,结果是生成器形式""" 5 arr.sort(reverse=True) # 从大到小排序 6 for x in combinations(arr[1:], len(arr[1:]) // 2): 7 yield arr[0:1] + list(x) #捆绑法,捆绑一个最大值 8 9 def get_abs_mins(avs,arr): 10 """返回各个list平均值与所有数平均值差的绝对值迭代对象""" 11 for x in arr: 12 abs_min = abs(sum(x)/len(x)-avs) 13 yield abs_min 14 if abs_min == 0: #绝对值为0时,停止比较 15 break 16 17 def run(arr1,arr2): 18 arr = arr1 + arr2 #合并list 19 avs = sum(arr) / len(arr) # 求所有数的平均值 20 abs_min = min(get_abs_mins(avs, get_combinations(arr))) # 求组合后的平均值与所有值平均值差的最小值 21 22 i = 1 23 for x in get_combinations(arr): 24 if abs(sum(x) / len(x) - avs) == abs_min: 25 x_ = arr[:] 26 for e in x: 27 x_.remove(e) #删除x中的元素 28 # i为比较次数,x是第一个list,x_是第2个list 29 print("循环次数:%s\nlist1:%s\nlist2:%s\nlist1求和:%s\nlist2求和:%s"%(i,list(x),x_,sum(x),sum(x_))) 30 return x,x_ 31 break 32 i += 1 33 34 if __name__ == ‘__main__‘: 35 a = [100, 99, 98, 1, 2, 3] 36 b = [1, 2, 3, 4, 5, 40] 37 try: 38 x,x_ = run(arr1=a,arr2=b) 39 # print(x) 40 # print(x_) 41 except: 42 pass
运行结果:
循环次数:337 list1:[100, 40, 5, 4, 3, 3] list2:[99, 98, 2, 2, 1, 1] list1求和:155 list2求和:203
改变a、b的值
a = [100, 99, 98, 1, 2, 3] + [(i+2)*(i+7) for i in range(6)]
b = [1, 2, 3, 4, 5, 40] + [(i+2)**2 for i in range(6)]
运行结果:
循环次数:201546 list1:[100, 99, 98, 66, 5, 4, 4, 3, 3, 2, 1, 1] list2:[84, 50, 49, 40, 36, 36, 25, 24, 16, 14, 9, 2] list1求和:386 list2求和:385
时间: 2024-10-12 08:50:25