如何解决递归填充动态尺寸向量
| 也许让我先用伪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恰好对返回的向量进行了concat。基本上,我以某种方式对函数进行采样,以使两个保存的函数值之间只有很小的差异。
我对上述方法不满意,因为在每个递归步骤中似乎都有大量的内存分配(分配两个子向量,然后将那些和另一个元素连接在一起)。这段代码必须在我的算法的一部分中运行,这对于性能至关重要。一旦upper - lower
很小,评估evaluating2ѭ就不会花费大量时间。
所以我的问题是:
您看到在所有递归调用中使用相同数据结构并仅填充该向量的当前部分的巧妙方法吗? (请记住,尚不知道所需的功能评估数量)。关于此的想法:
使用列表而不是向量。但是我觉得内存大修不足以存储双打。
在向量中保留孔,并维护另一个向量,说明哪些条目已填充。递归调用的末尾将条目移位,以使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]。因此,在下一步(递归)中,我评估evaluate10。现在,递归重复上述逻辑。
最后,我想(两个)向量为我提供具有相应参数的函数值,如下所示:
parameter = vector(0,50,56.25,62.5,75,100)
value = vector(0,10,17.21,25 34,40)
我正在寻找最好的(也是最高效的)方法来递归地构建这些向量。
希望这可以澄清事情。
解决方法
由于空间不是您主要关心的问题,因此我将继续使用递归。
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);
}
}
, 我建议采用以下方法:
不要使用两个向量,而是使用一对成对的向量或自定义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,out);
}
}
上面是对伪代码的修改,显示了使用输出迭代器时的伪代码。它未经测试,所以我不确定它是否正确。原则上,您可以这样称呼它:
std::vector<eval_point> results;
sample(someFunction,100,std::back_inserter<eval_point>(results));
这样,您将不必为每个递归调用创建新的向量。如果您可以猜测样本数量的合理下限,则可以进行预分配,从而无需重新分配。在这种情况下,您可以这样称呼它:
std::vector<eval_point> results(lower_bound_for_samples);
sample(someFunction,results.begin());
然后,您将不得不添加一个额外的计数器来跟踪生成了多少样本。
, 我不明白您为什么拒绝列表解决方案。
最坏的情况是列表的大小是原始数据的3倍。
我认为这远远少于在每个函数调用上创建一个新向量时的情况。
您应该尝试一下,因为它不需要太多更改,因为两者的界面几乎相同。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。