PHP线段填充算法

线段填充算法

最近做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实验

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值