接前面 求balance_list【一】,这一篇来讲(不太推荐的)递归、sum();
递归
def fun_recursive(list_index, amount_list, opening):
if list_index == 0:
return round(opening + amount_list[0], 2)
else:
return round(fun_recursive(list_index - 1, amount_list, opening) + amount_list[list_index], 2)
数万交易,是不是也这样呢?
实际,递归次数多了,会报错 maximum recursion depth exceeded;
这问题,咋解决?
答案:设置递归次数
代码如下:
import sys
sys.setrecursionlimit(3100)
这儿设置为3100次,实际我电脑在3500次左右,内存会吃不消;【下图和前面 是同样的代码,只是设置了setrecursionlimit】
倘若 交易量真为 数万笔呢?
思路:以3000笔为一组,分组来计算 【考虑到我设置的递归限制】
def fun_recursive_2(tot):
import random
import math
amount_list = random.sample(range(6000, 6000000), tot)
opening = 5000
Log.info('总共的订单数:{}'.format(tot))
if len(amount_list) <= 3000: # 将3000作为1次递归的长度
Log.info('订单数 不超3000')
balance_value = [fun_recursive(d, amount_list, opening) for d in range(len(amount_list))]
Log.info(balance_value)
else:
ci_shu = int(math.ceil(len(amount_list) / 3000)) # 加不加int一样
Log.info('迭代次数 {}'.format(ci_shu))
last_list = [list() for _ in range(ci_shu)]
for c in range(ci_shu):
if c == 0:
Log.info('{} - {} '.format(c * 3000, (c + 1) * 3000))
new_list = amount_list[c * 3000:(c + 1) * 3000]
first = [fun_recursive(i, new_list, opening) for i in range(len(new_list))]
last_list[c].extend(first)
else:
Log.info('{} - {} '.format(c * 3000, (c + 1) * 3000))
new_list = amount_list[c * 3000:(c + 1) * 3000]
other = [fun_recursive(i, new_list, last_list[c - 1][-1]) for i in range(len(new_list))]
last_list[c].extend(other)
Log.info('每一笔交易后的 手上的钱数, 以3000笔为一组,:{}'.format(last_list))
Log.info('直接sum() 计算的结果:{}'.format(sum(amount_list) + opening))
Log.info('多次递归,最后一组 最后一笔 钱数:{}'.format(last_list[-1][-1]))
计算过程和最后的结果:
若 我只想看到balance_list,不想划分组?
def fun_recursive_3(amount_l, opening_balance):
import math
import sys
sys.setrecursionlimit(3100)
Log.info('总共的订单数:{}'.format(len(amount_l)))
if len(amount_l) <= 3000: # 将3000作为1次递归的长度
balance_list = [fun_recursive(d, amount_l, opening_balance) for d in range(len(amount_l))]
else:
ci_shu = int(math.ceil(len(amount_l) / 3000))
Log.info('迭代次数 {}'.format(ci_shu))
balance_list = list()
for c in range(ci_shu):
if c == 0:
Log.info('{} - {} '.format(c * 3000, (c + 1) * 3000))
new_list = amount_l[c * 3000:(c + 1) * 3000]
first = [fun_recursive(i, new_list, opening_balance) for i in range(len(new_list))]
balance_list.extend(first)
else:
Log.info('{} - {} '.format(c * 3000, (c + 1) * 3000))
new_list = amount_l[c * 3000:(c + 1) * 3000]
other = [fun_recursive(i, new_list, balance_list[-1]) for i in range(len(new_list))]
balance_list.extend(other)
Log.info('迭代结束')
# Log.info(balance_list)
Log.info('直接sum() 计算的结果:{}'.format(sum(amount_l) + opening_balance))
Log.info('多次递归,最后一笔 钱数:{}'.format(balance_list[-1]))
sum()
递归那一块的代码,最后面 我写的是 直接sum() 计算结果:{}'.format(sum(amount_list) + opening)。
那sum()来做这个需求是不是更好呢?
result=sum(iterable[, start])
sum() 是返回序列iterable的总和,可选参数start表示从该值开始加起来,默认为0;
def sum_balance_list(amount_list=None):
if amount_list is None:
amount_list_1 = [-10.5, 20.6, -15, 124.8, -156]
else:
amount_list_1 = amount_list
Log.info('amount_list 获取完成')
open_b = 50
balance = [round(sum(amount_list_1[:a], open_b), 2) for a in range(1, len(amount_list_1) + 1)]
print(balance)
如果跑几十万笔呢?我这边是 迟迟拿不到结果;(可能是我电脑配置不够的原因)
把每次传的amount_list 改动下:
def sum_balance_list_2(amount_list=None):
import math
if amount_list is None:
amount_list_1 = [-10.5, 20.6, -15, 124.8, -156]
else:
amount_list_1 = amount_list
Log.info('amount_list 获取完成:{}'.format(amount_list_1))
open_b = 50
# 设置1w次的amount做一次计算
ci = int(math.ceil(len(amount_list_1) / 10000))
last_list = list()
for c in range(ci):
Log.info('当前循环的是 {} - {}'.format(c * 10000, (c+1) * 10000))
am_list = amount_list_1[c * 10000: (c+1) * 10000]
if c == 0:
last_list = [sum(am_list[:a], open_b) for a in range(1, len(am_list) + 1)]
Log.info(last_list)
else:
la = [sum(am_list[:a], last_list[-1]) for a in range(1, len(am_list) + 1)]
Log.info(la)
last_list.extend(la)
Log.info('直接sum() 计算结果:{}'.format(sum(amount_list) + open_b))
Log.info('最后一笔交易后的钱数:{}'.format(last_list[-1]))
递归【2】
import random
import math
import sys
sys.setrecursionlimit(3500)
am = random.sample(range(1, 600000), 350000)
op = 5000
# print(am)
def recursive(n, test_dict, amount_list, last_n, test_time):
if n not in test_dict:
x = n - 1
value = recursive(x, test_dict, amount_list, last_n, test_time) + amount_list[x - test_time * 300 - 1]
test_dict[n] = value
if n == last_n:
# print(test_dict)
return test_dict
return test_dict[n]
times = math.ceil(len(am) / 300) # 递归深度
print(times)
test_d = dict()
for t in range(1, times + 1):
abc = 300
t_time = (t -1) * abc
# print(t_time)
new_am = am[t_time: t_time + abc]
if t != 1:
# print(t_time)
op = res_n[t_time]
# print(op)
if t == times:
last = len(am)
else:
last = abc * t
# print(abc * t, 1 + (t-1) * abc, new_am[0] + op, last)
res_n = recursive(last, {1 + (t-1) * abc: new_am[0] + op}, new_am, last, t-1)
test_d.update(res_n)
print(test_d, len(test_d))
代码 为什么这样写,我没法讲清楚,因为我也是半吊子;【待我找时间 补足下 20200823留】
# 在python中的递归,可以使用字典来代替这个表
# 斐波那契数列:
# 当n = 1, 2时 f(n) = 1
# 当n > 2时 f(n) = f(n - 1) + f(n - 2)
# 比如:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55...]
def fun_recursive_2(n, test_dict):
Log.info('递归开始 {}'.format(n))
if n in test_dict:
Log.debug('{} 找到了'.format(n))
Log.info(test_dict)
return test_dict[n]
else:
Log.debug('这 {} 没找到'.format(n))
value = fun_recursive_2(n - 1, test_dict) + fun_recursive_2(n - 2, test_dict)
test_dict[n] = value
Log.debug(test_dict)
return test_dict[n]
new_dict = {1: 1, 2: 1}
r = fun_recursive_2(40, new_dict) # 求第40位的数字
print(r)
部分日志如下:
总结
本期 从实现过程来看:我用sum() ,是有很多重复:假设500笔订单,会做了个长度为500的循环,每次遍历,都会求当前金额+前面金额的sum,但第500次求和过程,是不是已经要算出前面499次的? 正常而言,做最后一次计算,就已经得出前面过程 所有的sum了, 那个循环 完全没必要。此外,递归的用法还得再升级。