【发布时间】:2018-06-23 06:33:18
【问题描述】:
动机
请看下图。
给出的是红色、蓝色和绿色曲线。我想在x 轴上的每个点找到主导曲线。这在图片中显示为黑色图表。根据红色、绿色和蓝色曲线的属性(一段时间后增加和恒定),这归结为在最右侧找到主导曲线,然后向左侧移动找到所有交点并更新主导曲线曲线。
这个概述的问题应该解决T 次。问题有一个最后的转折点。下一次迭代的蓝色、绿色和红色曲线是通过上一次迭代的主导解加上一些变化的参数来构造的。作为上图中的示例:解决方案是黑色功能。此函数用于生成新的蓝色、绿色和红色曲线。然后问题再次开始,以找到这些新曲线的主导曲线等。
问题简述
在每次迭代中,我从固定的最右侧开始,评估所有三个函数,看看哪个是主导函数。这种评估在迭代过程中花费的时间越来越长。 我的感觉是,我没有最佳地传递旧的主导功能来构建新的蓝色、绿色和红色曲线。原因:我在早期版本中遇到了最大递归深度错误。 代码的其他部分需要当前支配函数的值(这对于绿色、红色或蓝色曲线都是必不可少的)也需要随着迭代越来越长。
对于 5 次迭代,仅在最右侧的一点上评估函数就会增长:
结果是通过
test = A(5, 120000, 100000)
然后运行
test.find_all_intersections()
>>> test.find_all_intersections()
iteration 4
to compute function values it took
0.0102479457855
iteration 3
to compute function values it took
0.0134601593018
iteration 2
to compute function values it took
0.0294270515442
iteration 1
to compute function values it took
0.109843969345
iteration 0
to compute function values it took
0.823768854141
我想知道为什么会这样,以及是否可以更有效地对其进行编程。
详细代码说明
我快速总结了最重要的功能。完整的代码可以在下面找到。如果对代码有任何其他问题,我很乐意详细说明/澄清。
方法
u:用于生成新批次的重复任务 上面的绿色、红色和蓝色曲线我们需要旧的主导曲线。u是在第一次迭代中使用的初始化。方法
_function_template:函数生成版本 绿色、蓝色和红色曲线通过使用不同的参数。它返回 单个输入的函数。方法
eval:这是每次生成蓝色、绿色和红色版本的核心函数。每次迭代需要三个不同的参数:vfunction,这是上一步的主要函数,m,和s,这是影响结果曲线形状的两个参数(flaots)。其他参数在每次迭代中都相同。在代码中,每次迭代都有m和s的示例值。对于更怪异的:这是一个近似积分,其中m和s是基础正态分布的预期均值和标准差。近似是通过 Gauss-Hermite 节点/权重完成的。方法
find_all_intersections:这是在 每次迭代都是主导的。它构建了一个主宰 通过蓝色、绿色和红色的分段连接来发挥作用 曲线。这是通过函数piecewise实现的。
这是完整的代码
import numpy as np
import pandas as pd
from scipy.optimize import brentq
import multiprocessing as mp
import pathos as pt
import timeit
import math
class A(object):
def u(self, w):
_w = np.asarray(w).copy()
_w[_w >= 120000] = 120000
_p = np.maximum(0, 100000 - _w)
return _w - 1000*_p**2
def __init__(self, T, upper_bound, lower_bound):
self.T = T
self.upper_bound = upper_bound
self.lower_bound = lower_bound
def _function_template(self, *args):
def _f(x):
return self.evalv(x, *args)
return _f
def evalv(self, w, c, vfunction, g, m, s, gauss_weights, gauss_nodes):
_A = np.tile(1 + m + math.sqrt(2) * s * gauss_nodes, (np.size(w), 1))
_W = (_A.T * w).T
_W = gauss_weights * vfunction(np.ravel(_W)).reshape(np.size(w),
len(gauss_nodes))
evalue = g*1/math.sqrt(math.pi)*np.sum(_W, axis=1)
return c + evalue
def find_all_intersections(self):
# the hermite gauss weights and nodes for integration
# and additional paramters used for function generation
gauss = np.polynomial.hermite.hermgauss(10)
gauss_nodes = gauss[0]
gauss_weights = gauss[1]
r = np.asarray([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1.])
m = [[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.03836174909668277, 0.08543620707856969, 0.15548297423808233],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.03836174909668277, 0.08543620707856969, 0.15548297423808233],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.03836174909668277, 0.08543620707856969, 0.15548297423808233],
[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.038063407778193614, 0.08475713587463352, 0.15420895520972322],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.03836174909668277, 0.08543620707856969, 0.15548297423808233],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624],
[0.038212567720998125, 0.08509661835487026, 0.15484578903763624]]
s = [[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019529101011406914, 0.04708607140891122, 0.20089341636351565],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.019529101011406914, 0.04708607140891122, 0.20089341636351565],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019529101011406914, 0.04708607140891122, 0.20089341636351565],
[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.01945441966324046, 0.04690600929081242, 0.200125178687699],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019529101011406914, 0.04708607140891122, 0.20089341636351565],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142],
[0.019491796104351332, 0.04699612658674578, 0.20050966545654142]]
self.solution = []
n_cpu = mp.cpu_count()
pool = pt.multiprocessing.ProcessPool(n_cpu)
# this function is used for multiprocessing
def call_f(f, x):
return f(x)
# this function takes differences for getting cross points
def _diff(f_dom, f_other):
def h(x):
return f_dom(x) - f_other(x)
return h
# finds the root of two function
def find_roots(F, u_bound, l_bound):
try:
sol = brentq(F, a=l_bound,
b=u_bound)
if np.absolute(sol - u_bound) > 1:
return sol
else:
return l_bound
except ValueError:
return l_bound
# piecewise function
def piecewise(l_comp, l_f):
def f(x):
_ind_f = np.digitize(x, l_comp) - 1
if np.isscalar(x):
return l_f[_ind_f](x)
else:
return np.asarray([l_f[_ind_f[i]](x[i])
for i in range(0, len(x))]).ravel()
return f
_u = self.u
for t in range(self.T-1, -1, -1):
print('iteration' + ' ' + str(t))
l_bound, u_bound = 0.5*self.lower_bound, self.upper_bound
l_ordered_functions = []
l_roots = []
l_solution = []
# build all function variations
l_functions = [self._function_template(0, _u, r[t], m[t][i], s[t][i],
gauss_weights, gauss_nodes)
for i in range(0, len(m[t]))]
# get the best solution for the upper bound on the very
# right hand side of wealth interval
array_functions = np.asarray(l_functions)
start_time = timeit.default_timer()
functions_values = pool.map(call_f, array_functions.tolist(),
len(m[t]) * [u_bound])
elapsed = timeit.default_timer() - start_time
print('to compute function values it took')
print(elapsed)
ind = np.argmax(functions_values)
cross_points = len(m[t]) * [u_bound]
l_roots.insert(0, u_bound)
max_m = m[t][ind]
l_solution.insert(0, max_m)
# move from the upper bound twoards the lower bound
# and find the dominating solution by exploring all cross
# points.
test = True
while test:
l_ordered_functions.insert(0, array_functions[ind])
current_max = l_ordered_functions[0]
l_c_max = len(m[t]) * [current_max]
l_u_cross = len(m[t]) * [cross_points[ind]]
# Find new cross points on the smaller interval
diff = pool.map(_diff, l_c_max, array_functions.tolist())
cross_points = pool.map(find_roots, diff,
l_u_cross, len(m[t]) * [l_bound])
# update the solution, cross points and current
# dominating function.
ind = np.argmax(cross_points)
l_roots.insert(0, cross_points[ind])
max_m = m[t][ind]
l_solution.insert(0, max_m)
if cross_points[ind] <= l_bound:
test = False
l_ordered_functions.insert(0, l_functions[0])
l_roots.insert(0, 0)
l_roots[-1] = np.inf
l_comp = l_roots[:]
l_f = l_ordered_functions[:]
# build piecewise function which is used for next
# iteration.
_u = piecewise(l_comp, l_f)
_sol = pd.DataFrame(data=l_solution,
index=np.asarray(l_roots)[0:-1])
self.solution.insert(0, _sol)
return self.solution
【问题讨论】:
-
我认为你的问题对 SO 来说太大了。虽然我可能会花费数小时测试和编写答案,但我很少在第一次阅读上花费超过 30 秒。
-
@hpaulj 我试图尽可能地减少它。它现在是一个玩具示例,但显示了我在扩展版本中遇到的行为。请让我知道它是否更好或如何改进它
-
如果您正在开发新代码,您的目标绝对应该是 Python 3,而不是 2.7
-
看起来这只是一个递归问题 - 您的
eval/vfunction每次迭代都会增加复杂性,好像需要重新评估所有底层和前面的函数。 -
基于快速浏览,如果您不断调用前一个函数并且函数相同,也就是相同的子组件。然后它非常类似于动态编程方法,例如背包。您可以使用参数输入作为索引来具体化先前的函数结果。然后每个函数执行结果查找(如果结果已经计算)和计算(如果结果未计算)。
标签: python algorithm python-2.7 numpy scipy