也许让我在伪C ++中陈述我的情况 - 首先是代码:
std:vector<double> sample(someFunctor f, double lower, double upper) {
double t = (lower + upper)/2;
double newval = f(t);
if (f(upper) - newval > epsilon)
subsample1 = sample(f, t, upper);
if (newval - f(lower) > epsilon)
subsample2 = sample(f, lower, t);
return concat(subsample2, newval, subsample1);
}
其中concat,好吧,汇总返回的向量。基本上,我正在以一种方式对函数进行采样,使得在两个保存的函数值之间只有很小的差异。
我对上述方式不满意,因为在每个递归步骤中似乎都有相当多的内存分配(分配两个子向量,然后连接那些和另一个元素)。这段代码必须在我的算法的一部分中运行,这对性能至关重要。 upper - lower
相当小后,评估f
不会花费大量时间。
所以我的问题:
您是否看到了在所有递归调用中使用相同数据结构的巧妙方法,并且只填充该向量的当前部分? (请记住,所需的功能评估数量尚未预先知道)。对此的想法:
subsample
和newval
之间没有空洞。但是现在我通过为第二个向量添加额外的工作来切换复制 - 可能是个坏主意。你是否看到了完全摆脱递归的方法?但是,为了正确起见,我必须使用上述分而治之的模式。函数f
大量使用上限和下限,并获得了相当大的性能。
感谢您的想法。
根据Space_C0wb0y的请求,我试着改写一下我的问题。也许第一个解释不是很清楚。
我有一些功能(在数学意义上)我想在给定的间隔内采样(例如在某些点评估)。
假设间隔为[0,100]。我知道函数值在0和100处。可能是f(0)=0
和f(100) = 40
。
现在我在间隔中点评估函数,即50.我的函数返回f(50)=10
。
如f(0)-f(50) <= 10
,我在区间[0,50]中不需要进一步的样本。但是,我需要进一步计算间隔[50,100]。因此,在下一个(递归)步骤中,我评估f(75)
。现在递归地重复上述逻辑。
最后我想(两个)向量给我带有相应参数的函数值,如下所示:
parameter = vector(0, 50, 56.25, 62.5, 75, 100)
value = vector(0, 10, 17.21, 25 34, 40)
我正在寻找以递归方式构建这些向量的最佳(最高效)方法。
希望这能澄清事情。
答案 0 :(得分:2)
由于空间不是您的主要关注点,因此我将继续使用递归。
<强> 1。按引用使用副本而不是按(返回)值复制。
<强> 2。不需要传递仿函数,因为它不变。
第3。如果low
和high
是整数,则可能会更快。这取决于要求。
// Thanks to Space_C0wb0y, here we avoid using a global vector
// by passing the vector as reference. It's efficient as there
// is no copy overhead as well.
void sample(vector<double>& samples, double low, double high)
{
// You can use shift operator if they're integers.
double mid = (low + high)/2;
// Since they're double, you need prevent them from being too close.
// Otherwise, you'll probably see stack overflow.
// Consider this case:
// f(x): x=1, 0<x<8; x*x, x<=0 or x>=8
// low = 1, high = 10, epsilon = 10
if (high - low < 0.5)
{
samples.push_back(f(mid));
return;
}
// The order you write the recursive calls guarantees you
// the sampling order is from left to right.
if (f(mid) - f(low) > epsilon)
{
sample(samples, low, mid);
}
samples.push_back(f(mid));
if (f(high) - f(mid) > epsilon)
{
sample(samples, mid, high);
}
}
答案 1 :(得分:1)
我建议采用以下方法:
不要使用两个向量,而是使用一个带有对的向量或自定义struct
来表示参数和值:
struct eval_point {
double parameter;
double value;
};
std::vector<eval_point> evaluated_points;
更改算法以将评估结果写入输出迭代器:
template<class F, class output_iterator_type>
void sample(F someFunctor, double lower, double upper,
output_iterator_type out) {
double t = (lower + upper)/2;
eval_point point = { t, f(t) };
if (f(upper) - point.value > epsilon) {
*out = point;
++out;
sample(f, t, upper, out);
}
if (point.value - f(lower) > epsilon) {
*out = point;
++out;
subsample2 = sample(f, lower, t, out);
}
}
以上是对伪代码的修改,显示了使用输出迭代器时的样子。它没有经过测试,所以我不确定它是否正确。原则上,你会这样称呼它:
std::vector<eval_point> results;
sample(someFunction, 0, 100, std::back_inserter<eval_point>(results));
这样您就不必为每个递归调用创建新的向量。如果您可以猜测样本数量的合理下限,则可以预先分配,以便不需要重新分配。在这种情况下,你会这样称呼它:
std::vector<eval_point> results(lower_bound_for_samples);
sample(someFunction, 0, 100, results.begin());
然后,您必须添加一个额外的计数器来跟踪生成的样本数量。
答案 2 :(得分:0)
我不明白为什么拒绝列表解决方案。 最糟糕的情况是您的列表的大小是原始数据的3倍。 我认为这比你在每个函数调用上创建一个新向量时要少得多。 您应该尝试一下,因为它不需要那么多的改变,因为两者的界面几乎相同。