Php 获取免费IP地址的算法
我使用yii2。我需要通过getFreeIPAddress方法找到数据库中未使用的IP。我有这样的课:Php 获取免费IP地址的算法,php,mysql,algorithm,optimization,yii2,Php,Mysql,Algorithm,Optimization,Yii2,我使用yii2。我需要通过getFreeIPAddress方法找到数据库中未使用的IP。我有这样的课: class Radreply extends ActiveRecord { const ATTRIBUTE_DEFAULT_IP_ADDRESS = 'Framed-IP-Address'; const IP_ADDRESS_MAX = '10.255.255.255'; // max value for IP const IP_ADDRESS_MIN = '10.0
class Radreply extends ActiveRecord {
const ATTRIBUTE_DEFAULT_IP_ADDRESS = 'Framed-IP-Address';
const IP_ADDRESS_MAX = '10.255.255.255'; // max value for IP
const IP_ADDRESS_MIN = '10.0.0.11'; // min value for IP
public function getIntegerIP(){ // converts IP from string to integer format
return ip2long($this->value);
}
public static function getFreeIPAddress(){
$records = self::findAll(['attribute'=>self::ATTRIBUTE_DEFAULT_IP_ADDRESS]); // get all record which contain IP address
$existIPs = ArrayHelper::getColumn($records,'integerIP'); // get array of IP which is converted to integer by method getIntegerIP
for ($integerIP = ip2long(self::IP_ADDRESS_MIN); $integerIP<=ip2long(self::IP_ADDRESS_MAX); $integerIP++){
// increasing one by one IP address in integer format from value IP_ADDRESS_MIN to value IP_ADDRESS_MAX
if (!in_array($integerIP, $existIPs)){
$stringIP = long2ip($integerIP);
$arrayDigits = explode('.', $stringIP);
$lastDigit = array_pop($arrayDigits);
if ($lastDigit!='0'){ // check if last digit of IP is not 0
return $stringIP;
}
}
}
return '';
}
}
方法getFreeIPAddress可以找到,但是在db中有很多IP记录,并且一个接一个地增加IP,检查db中是否存在这个IP还有很长的路要走。如何优化这个算法?有没有更快的方法获取未使用的IP
bool in_数组混合$needle,数组$haystack[,bool$strict=FALSE]
在我看来,你可以设定严格的真实性
我的php代码带有strict=false
当严格是真的
更重要的是,如果你能以升序获得使用过的ip。您可以得到om+n时间复杂度,m是您应该尝试的所有ip的长度,n是使用合并算法的所有ip的长度(以db为单位)
如果您可以按升序获取已使用的ip
在伪代码中
这是我的php代码,其中我用$count++重新封装了EchoIP;
在这个演示中,大约有80000个ip,类型为long
大约需要10秒;
time php test.php
184460881
实际0m10.626s
用户0m10.416s
sys 0m0.168s我想,我已经找到了更好的解决方案,而不需要在数据库中添加额外的表
class Radreply extends ActiveRecord {
const ATTRIBUTE_DEFAULT_IP_ADDRESS = 'Framed-IP-Address';
const IP_ADDRESS_MAX = '10.255.255.255'; // max value for IP
const IP_ADDRESS_MIN = '10.0.0.11'; // min value for IP
public function getIntegerIP(){ // converts IP from string to integer format
return ip2long($this->value);
}
public static function getFreeIPAddress(){
$records = self::findAll(['attribute'=>self::ATTRIBUTE_DEFAULT_IP_ADDRESS]); // gets all record which contain IP address
$existIPs = ArrayHelper::getColumn($records,'integerIP'); // gets array of IP which is converted to integer by method getIntegerIP
$intIpAddressMin = ip2long(self::IP_ADDRESS_MIN); // gets min IP in integer format
$endRange = empty($existIPs) ? $intIpAddressMin : max($existIPs); // checks if at least one IP is used
$availableIPs = range( $intIpAddressMin, $endRange + 2); // generates array with available IP addresses (+2 because next address can be with last digit 0)
$missingIPs = array_diff($availableIPs,$existIPs); // removes all used IP
foreach ($missingIPs as $value){
$lastDigit = $value % 256;
if ($lastDigit != 0){
return long2ip($value);
}
}
return '';
}
}
嗯,您可以创建一个包含所有可用IP地址的表,然后右键连接到另一个表以获得免费IP的列表addresses@cmorrissey谢谢,好主意。但如果我想更改可用IP地址的范围,就会出现问题,每次我都需要创建新表。此外,这个表将非常大:最小值-10.0.0.11',最大值-10.255.255'16 777 204条记录。我有一个新想法给你。。。在一个IP地址范围内计算数据库中的行数,如果不转到下一个范围,则查看是否有可用的IP地址,并且可以递归地缩小范围。这是一个非常基本的搜索函数,但它应该以指数方式加快搜索速度。@cmorrissey我不明白你的真正意思。你能详细描述一下吗?但无论如何,谢谢,计数这是个好主意,我认为计数是该算法优化的关键。按照@cmorrissey的建议,创建一个可用IP地址表。所以,如果它很大,有1600万行,它仍然是16兆字节+SQL数据库的任何开销。此外,如果需要向池中添加更多地址,则无需创建新表,只需向该表中插入更多行即可!此外,如果您使用的是MySQL,您可能希望使用INET_ATON和INET_NTOA在查询中转换整数。不过,不要加入,只要在地址用完时删除地址即可。谢谢你的回答。你所说的合并算法是什么意思?我有一个问题,你只需要一个未使用的ip,或者一次性获得所有未使用的ip?只有一个未使用的ip地址你可以得到10000个未使用的ip地址,并通过合并算法保存到新表中。当您想要一个未使用的ip时,您只需从这个新表中获取ip,并向使用过的旧表中添加一行即可。每次新表为空时,使用合并算法获得新的10000行。您能看看我的新解决方案吗?
real 0m4.418s
user 0m4.404s
sys 0m0.012s
for($i=0;$i<30000;$i++){
if(in_array($y,$x ,true)){
continue;
}
}
real 0m1.548s
user 0m1.540s
sys 0m0.004s
tmpIp = minIp;
while(temIp <= maxIp){
if( dbIsEmpty){
break;
}
dbIp =getNextFromDb();
while(temIp < dbIp){
printf temIp ;
temIp ++;
}
temIp ++;
}
while(temIp <= maxIp){
printf temIp ;
temIp++;
}
<?php
function mergeSort( $result){
$minIp = ip2long('10.0.0.11') ;
$maxIp = ip2long('10.255.255.255');
$count =0;
$tmpIp = $minIp;
while($temIp <= $maxIp){
if( empty($result)){
break ;
}
$tmp = array_pop($result);
$dbIp =$tmp['ip'];
while($temIp < $dbIp){
// echo temIp ;
// i repalce it by count ++ , i don't want it
//full my teminal .
$count ++;
$temIp ++;
}
$temIp ++;
}
while($temIp <= $maxIp){
//echo $temIp ; replace by $count++
$count ++;
$temIp++;
}
return $count -1;
}
$servername = "localhost";
$username = "root";
$password = "aaaaa";
$dbname = "IP";
$conn = new PDO('mysql:host=' . $servername . ';dbname=' . $dbname , $username, $password);
$conn->setAttribute(PDO::ATTR_AUTOCOMMIT , true);
$stmt = $conn->prepare("select * from ipTable order by ip desc");
$stmt->execute();
$result = $stmt->fetchAll();
$count = mergeSort($result);
echo $count ;
?>
class Radreply extends ActiveRecord {
const ATTRIBUTE_DEFAULT_IP_ADDRESS = 'Framed-IP-Address';
const IP_ADDRESS_MAX = '10.255.255.255'; // max value for IP
const IP_ADDRESS_MIN = '10.0.0.11'; // min value for IP
public function getIntegerIP(){ // converts IP from string to integer format
return ip2long($this->value);
}
public static function getFreeIPAddress(){
$records = self::findAll(['attribute'=>self::ATTRIBUTE_DEFAULT_IP_ADDRESS]); // gets all record which contain IP address
$existIPs = ArrayHelper::getColumn($records,'integerIP'); // gets array of IP which is converted to integer by method getIntegerIP
$intIpAddressMin = ip2long(self::IP_ADDRESS_MIN); // gets min IP in integer format
$endRange = empty($existIPs) ? $intIpAddressMin : max($existIPs); // checks if at least one IP is used
$availableIPs = range( $intIpAddressMin, $endRange + 2); // generates array with available IP addresses (+2 because next address can be with last digit 0)
$missingIPs = array_diff($availableIPs,$existIPs); // removes all used IP
foreach ($missingIPs as $value){
$lastDigit = $value % 256;
if ($lastDigit != 0){
return long2ip($value);
}
}
return '';
}
}