线段填充算法
最近做ABTest平台,每个实验会独占坐标轴的一段长度,假设C实验坐标起止点为[16,20](总长度5),当C实验停止后,假设来了F实验,F实验的所需要占坐标轴的长度为 15,我需要从C空出来的坐标开始填充,因为F实验大于C空出来的总长度,所以需要把F实验拆成N段,获取F实验的所有拆分的坐标起止点~,大概这么个意思吧,自己记录一下
$arr = [
[0, 0],
[60, 62],
[5, 10],
[38, 45],
[89, 92],
[63, 88],
[21, 27],
[15, 20],
[101, 101]
];
// // 填空算法
// usort($arr, function ($a, $b) {
// return $a[0] > $b[0];
// });
// // 先把数据排列整齐,按照区间段排列好
function sortArray($arr)
{
usort($arr, function ($a, $b) {
return $a[0] > $b[0];
});
return $arr;
}
function insert($arr, $segmentLen)
{
$len = count($arr);
$result = [];
for ($i = 0; $i < $len - 1; $i++) {
$a = $arr[$i];
$b = $arr[$i + 1];
if ($a[1] + 1 != $b[0]) {
$result[] = [$a[1] + 1, $b[0] - 1];
}
}
// 先看下剩余未分的容量有多少
$surplusCapacity = 0;
foreach ($result as $res) {
$surplusCapacity = $surplusCapacity + ($res[1] - $res[0]) + 1;
}
// 新段长度大于剩余容量的时候,无法再分
if ($segmentLen > $surplusCapacity) {
return false;
}
// 需要往里面插入的段
$insertSegment = [];
foreach ($result as $res) {
$capacity = $res[1] - $res[0] + 1; // 假设[1,2],闭区间长度是
if ($segmentLen <= $capacity) {
$insertSegment[] = [$res[0], $res[0] + $segmentLen - 1];
break;
}
// 剩余的未填充的段长度计算下
$segmentLen = $segmentLen - $capacity;
if ($segmentLen > 0) {
$insertSegment[] = [$res[0], $res[0] + $capacity - 1];
}
}
return sortArray(array_merge($arr, $insertSegment));
}
$newNodeLen = 40;
$arr = sortArray($arr);
$newArray = insert($arr, $newNodeLen);
print_r($newArray);
abtest有实验,实验组,样本(用户)这三个基本概念。
其中,每个实验下面至少有一个对照组和N个实验组,每个实验组需要拿一定量的样本来做实验得出结果。
我们把实验组所需要的样本数量抽象为桶的概念,假设我们有100个桶,代表全部(100%)样本,每个实验组可以自由设定占用多少个桶,即有百分之多少的用户能够落入当前实验组。
代码中我们用数组来表示区间,[1,10]我们理解为一个前后都是闭区间的范围,代表当前实验组占用了[1,2,3,4,5,6,7,8,9,10]这十个桶,也就是10%的流量,也就是假设有1w个用户能够参与当前实验,会有大概10%的人约1000人会命中实验组A,进入实验组A。看下图关系:
实验A配置:[
实验组A1: [ [1,10] ]
实验组A2: [ [11,20] ]
实验组A2: [ [21,30] ]
]
实验B配置:[
实验组B1:[ [31,35] ]
实验组B2:[ [36,40] ]
]
实验C配置:[
实验组C1: [ [41,50] ]
实验组C1: [ [51,60] ]
]
看起来好像一维数组好像满足了需求,为什么存储的时候每个实验组我们用了一个二维数组呢?
在编辑AB后台操作的时候,有这么一个场景。
目前通过后台添加了ABC三个实验,某一天B实验结束了,A和C实验还正常在运行,然后产品增加了一个D实验,D实验需求如下:
实验D下面有两个实验组,每个实验组占用15%流量。
B实验已经停止,所以实验B下面的两个实验组所占用的桶[[31,35], []36,40]]的桶(流量)就需要释放掉,然后留给其他的实验复用。
已知实验D下面一共两个实验组D1、D2,每个实验组要占用15%的流量,C空出来的区间[31, 35], [36,40] 一共才有10%的流量,
实验组D1需要先占用掉B空出来的实验区间,[31,40]共10个桶,还缺五个桶,他就要继续往后搜索,后面的C实验占用了[[41,50],[51,60]]区间段,目前没有释放,所以他要找到最后一个没有用的空间占用五个桶,即从[61, 65],这样的话,实验D的第一个实验他所占用的桶的位置就是[[31,40],[61,65]],一共15%的流量,实验组D2同理进行计算。拿到D实验组第二个流量的范围区间。
用一个二维数组的方式,我们可以用多段区间表示法,让我们的单个实验组填满其他实验已经空出来的桶,做到流量不浪费,我们计算用户命中实验的时候,只需要取出当前实验组存储的所有时间区间进行合并计算就可以了
用户进入系统以后,我们通过算法哈希得到一个值,用值%100+1, 获取的值肯定在[1,100]之间,再结合数据库中存储的对应值,假设D1 【[[31,40],[61,65]]】一共占了15%的流量,那么大概会有15%的样本落入D1实验