前言
正则采样,就是从原数组中尽量等间隔的取出采样点。
最近在学习MPI并行编程,今天手撕并行正则采样排序时遇到一个难题——如何实现正则采样? 这部分在CSDN上没人介绍,我自己琢磨了一种自适应的方法。其效果还没有进行大量测试,下面说一下我的思路。
代码
先贴出代码,后续讨论
void regular_samp(int* raw, int count, int* samp, int k)
{
int step = floor((count - k) / (k + 1)) + 1;
int j = 0, left = count;
for (int i = step - 1; i < count && j < k; i += step)
{
samp[j++] = raw[i];
left -= step;
step = round(double(left - (k - j)) / (k - j + 1)) + 1;
}
}
最初想法
上述代码实现的是从包含count个元素的raw数组中正则采样k个元素到samp数组中。
所谓正则采样,最理想的情况就是等间隔采样。
比如从 1 2 3 4 5 6 7中取3个数——2 4 6。间隔为2(两个采样数字之间的距离)
因此我最初的想法就是计算间隔(step)等于多少,从count个数中取出k个数,然后均分成k + 1段。如果下标从0开始计算,也就是说认为还有两个隐含的采样点 -1 和 count。
提出公式: s t e p = c o u n t − k k + 1 + 1 step= \frac{count - k}{k + 1} + 1 step=k+1count−k+1
但很多时候分数并不整除,这时候问题来了,对于分式的结果我们向上取整还是向下取整还是四舍五入?
按我的测试,这些方案都不合适。
比如 1 2 3 4 5取3个数,最理想的结果应该是1 3 5。如果向上取整,step = 2,干脆取不到三个数。如果向下取整,step = 1,取得的是1 2 3,很不合理。
自适应方法
基本思路还是按照step的大小决定数在哪里取得。但自适应方法,就是让算法依据子问题自动决定step的大小。每次取出一个数,直到取出所有数。
原问题是从包含count个元素的raw数组中正则采样k个元素到samp数组中。取出一个数后,问题变成从count - step个数中正则采样k - 1个数。
第一个step计算我使用了向下取整方式,因为-1那个默认的采样点是不存在的,这里的间隔可以损失一些。后面则采用四舍五入方法。
粗略验证
使用6个核对0 1 2 … 1022 1023排序(每次运行前提前打乱数组),采样结果为:
结语
按照我在并行正则采样排序中的实践,效果还是不错的。欢迎大家指出问题。