Mein Fehler, ich hab nicht genau hingeschaut. Aber mal angenommen, deine Lösung wäre funktionabel (das for fiele dabei sicher weg), dann wäre da zwar nur ein linearer Durchlauf zu sehen (ohne Array-Umformen), aber müsstest du dann nicht auch noch den internen Hash-Zugriff in die Bewertung einbeziehen, und bekäme dann nicht das Wort "weniger" eine größere Gewichtung?
Das kann ich aus dem Stegreif leider auch nicht beantworten. Ich rechne immer erst mal naiv mit O(1) für einen Lookup in so einer PHP Hash-Map. Das ist nicht der worst case. Bislang habe ich durch Hash-Maps immer den Performance-Zuwachs erreicht, den ich erwartet habe beziehungsweise der mir ausgereicht hat. (Performance-Fragen… Schwieriges Thema.) Ich hatte deshalb noch keine Notwendigkeit, mich mit den Nuancen zu befassen. Im Grunde ist das auch müßig, weil die ab einem gewissen Punkt ohnehin Implementationsdetail der Engine sind. Auf PHP-Ebene hat man es letztlich gar nicht so sehr in der Hand.
Hier mal meine vorläufige überarbeitete Version von f()
und g()
(der Einfachheit halber poste ich wieder alles):
(Rechnet mit Fehlern. ;) Ich habe gerade nicht die Muße, ordentliche Tests zu schreiben. Das ist knifflig für Netzmasken, deren Bitzahl nicht durch 8 teilbar ist. Der Code ist auch noch nicht wirklich durchdacht.)
<?php
$a = array(
'192.168.11.12/24',
'62.10.11.12/32',
'128.10.11.12/8',
'96.50.51.52/16',
'192.168.1.188/27'
);
$b = array(
'192.168.128.0', // kein Treffer
'192.168.11.255', // Treffer
'62.10.11.255', // kein Treffer
'62.10.11.12', // Treffer
'128.255.255.255', // Treffer
'129.255.255.255', // kein Treffer
'96.50.255.255', // Treffer
'97.50.51.52', // kein Treffer
'192.168.1.189', // Treffer
'192.168.1.89' // kein Treffer
);
function f(array $a)
{
$new = array();
foreach ($a as $item) {
list($ipv4, $cidr) = explode('/', $item);
$iplong = ip2long($ipv4);
$iplong >>= 32 - $cidr;
if (!isset($new[$cidr])) {
$new[$cidr] = array();
}
$new[$cidr][$iplong] = true;
}
return $new;
}
function g(array $a2, array $b)
{
foreach ($b as $item) {
$iplong = ip2long($item);
for ($i = 1; $i <= 32; $i++) {
if (isset($a2[$i][$iplong >> 32 - $i])) {
echo $item . '- Treffer' . '(' . $i . ')' . "\n";
continue 2;
}
}
echo $item . ' - kein Treffer' . "\n";
}
}
$a2 = f($a);
g($a2, $b);
→ Demo
Ansonsten würde ich aber auch den Usecase abwarten wollen, was weitere Optimierungen angeht (die mit Sicherheit möglich sind).
Edit: Unwesentliche Anpassung.