php 获取连续数字,记录一次百万连续ID获取中断数值的算法

场景介绍:有100万篇文章生成了100个html文件,后来删除部分文章,但是文件并未删除,通过遍历html文件所在目录然后根据已知的ID集合判断是否该删除文件导致IO访问过于频繁且执行时间极长。

算法介绍:

这里有100万个数字组成的ID,其中ID是自增但是不连续的,现在需要求出中断部分的ID集合。

思路:已知这100万个ID是有序但不连续的,可以尝试构造一个for循环,使用二分查找判断是否存在于这100万个元素中,如果不存在则加入到中断部分集合。

遇到的问题:由于数据源过大导致二分查找消耗内存巨大且运行时间非常长,需要改进思路。后来经过分析,这两个集合都是正序的,完全可以定义一个游标进行单层循环。

代码实现:<?php

set_time_limit(0);

header("Content-type: text/html; charset=utf-8");

$stime = microtime(true);

// 按照ID正序排列, 假设有1000000个ID, 1/10000几率中断

$checkIdListSize = 1000000;

$checkIdList = generateList($checkIdListSize, $checkIdListSize/10000);

p("初始: ".tosize(memory_get_usage()));

// 得到没有审核的ID集合

$uncheckIdList = array();

$cursorList = array();

// 使用偏移计数器

$offset = 0;

// 不能以$checkIdList的最小值和最大值作为边界,而应该以1和可能存在的最大ID作为边界

for($i = 1; $i

$cursor = $checkIdList[$offset];

if($i == $cursor) {

$offset++;

continue;

}else if($i 

$cursorList[] = $i;

}

}

// 打印中断的ID

p(implode(",", $uncheckIdList));

p(implode(",", $cursorList));

// 打印中断的ID

//p(implode(",", $uncheckIdList));

p("使用: ".tosize(memory_get_usage()));

p("峰值: ".tosize(memory_get_peak_usage()));

//获取程序执行结束的时间

$etime = microtime(true);

//计算差值

$total = $etime-$stime;

p("当前页面执行时间为:{$total} 秒");

/**

* 随机生成一个正序排列、不连续的数组

* $maxSize 数组大小

* $disruptedNum 中断、不连续的数量

*/

function generateList($maxSize, $disruptedNum) {

$checkIdList = array();

$shuffleKeys = array();

// 模拟这1000000个的key

for($i=0; $i

$shuffleKeys[] = $i;

$checkIdList[] = $i+1;

}

// 打乱顺序

shuffle($shuffleKeys);

// 随机删除其中10000个ID

$randomKeys = array_slice($shuffleKeys, 0, $disruptedNum);

// 设置为null保证$checkIdList的长度不会改变

foreach($randomKeys as $randomKey) {

$checkIdList[$randomKey] = null;

}

// 删除为null的元素并重新索引以保证后面进行二分查找时传入的是有序数组

$checkIdList = array_values(array_filter($checkIdList));

return $checkIdList;

}

/**

* 二分查找法, $array必须是已经排好序的数组

* $array为数据源

* $find为待查找的数字

*/

function binarySearch($array, $find) {

if(empty($array)) {

return -1;

}

$start = 0;

$end = count($array)-1;

while ($start <= $end) {

$mid = floor(($start + $end) / 2);

if ($array[$mid] == $find) {

return $mid;

}

if ($array[$mid] > $find) {

//left

$end = $mid - 1;

} else {

$start = $mid + 1;

}

}

return -1;

}

//打印变量

function p($var) {

echo "

";

if($var === false)

{

echo 'false';

}else if($var === null){

print_r('null');

}else if($var === ''){

print_r("''");

}else{

print_r($var);

}

echo "

";

}

/**

*@功能:将filesize大小转化为常用大小

*@参数:类型为number,$bytes是filesize结果,大小Byte

*@返回:类型为string

*/

function tosize($bytes) {

if ($bytes >= 1099511627776) {                  //如果提供的字节数大于等于2的40次方,则条件成立

$return = round($bytes / 1099511627776, 2); //将字节大小转换为同等的T大小

$suffix = 'TB';                             //单位为TB

} elseif ($bytes >= 1073741824) {               //如果提供的字节数大于等于2的30次方,则条件成立

$return = round($bytes / 1073741824, 2);    //将字节大小转换为同等的G大小

$suffix = 'GB';                             //单位为GB

} elseif ($bytes >= 1048576) {                  //如果提供的字节数大于等于2的20次方,则条件成立

$return = round($bytes / 1048576, 2);       //将字节大小转换为同等的M大小

$suffix = 'MB';                             //单位为MB

} elseif ($bytes >= 1024) {                     //如果提供的字节数大于等于2的10次方,则条件成立

$return = round($bytes / 1024, 2);          //将字节大小转换为同等的K大小

$suffix = 'KB';                             //单位为KB

} else {                                        //否则提供的字节数小于2的10次方,则条件成立

$return = $bytes;                           //字节大小单位不变

$suffix = 'Byte';                           //单位为Byte

}

return $return ." " . $suffix;                  //返回合适的文件大小和单位

}

--------------------------------------------------------------------------------------

当然,当数据量超过百万,很有可能出现一种情况,就是单个结果集就超过了内存上限,解决办法就是适当的切割大小。一般我们从数据库中查询出来的数据不能太大,使用limit做限制后得到的结果集比较小,所以还是可以用上面的方式获取中断的ID部分。

转载随意,但请附上文章地址:-)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值