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
    评论
要在Java中获取雪***算法实现。Snowflake算法生成的ID是一个64位的长整型数字,由以下几部分组成: 1. 时间戳(41位):记录生成ID的时间,精确到毫秒级别。 2. 工作机器ID(10位):标识不同的工作机器。 3. 序列号(12位):在同一毫秒内,按顺序递增生成的序列号。 以下是一个示例代码,演示如何使用Java获取雪花算法生成的ID: ```java public class SnowflakeIdGenerator { // 定义开始时间戳,可以根据自己的需求进行调整 private static final long START_TIMESTAMP = 1609459200000L; // 2021-01-01 00:00:00 // 定义每部分占用的位数 private static final long SEQUENCE_BITS = 12; // 序列号占用位数 private static final long WORKER_ID_BITS = 10; // 工作机器ID占用位数 private static final long TIMESTAMP_BITS = 41; // 时间戳占用位数 // 定义每部分的最大值 private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS); private static final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BITS); // 定义每部分向左的位移 private static final long WORKER_ID_SHIFT = SEQUENCE_BITS; private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; // 工作机器ID private long workerId; // 序列号 private long sequence = 0L; // 上次生成ID的时间戳 private long lastTimestamp = -1L; public SnowflakeIdGenerator(long workerId) { if (workerId > MAX_WORKER_ID || workerId < 0) { throw new IllegalArgumentException("Worker ID超出范围"); } this.workerId = workerId; } public synchronized long generateId() { long timestamp = System.currentTimeMillis(); if (timestamp < lastTimestamp) { throw new RuntimeException("时钟回拨异常"); } if (timestamp == lastTimestamp) { sequence = (sequence + 1) & MAX_SEQUENCE; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0; } lastTimestamp = timestamp; return ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT) | (workerId << WORKER_ID_SHIFT) | sequence; } private long tilNextMillis(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); } return timestamp; } } ``` 使用示例: ```java public class Main { public static void main(String[] args) { SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1L); // 设置工作机器ID为1 long id = idGenerator.generateId(); System.out.println("生成的ID:" + id); } } ``` 上述代码中,SnowflakeIdGenerator类封装了雪花算法的实现细节。在使用时,通过创建SnowflakeIdGenerator对象,并调用generateId()方法,即可生成一个雪花算法生成的ID。 注意:为了确保唯一性和正确性,请保证不同的工作机器ID(workerId)和不同的时间戳。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值