2009-12-21 9 views
13

Was wäre die effiziente Weise, jedes n-te Element aus einem großen Array auszuwählen? Gibt es eine "kluge" Art, dies zu tun, oder gibt es nur einen Looping?Auswählen jedes n-ten Elements aus einem Array

einige Punkte zu beachten:

  • Das Array ist durchaus mit 130 000 Stück große
  • I
  • jedes 205. Element wählen haben
  • Die Elemente numerisch nicht indiziert sind, wird so nicht funktionieren for($i = 0; $i <= 130000; $i += 205)

Bisher ist dies die effizienteste Methode, die ich habe kommen mit:

$result = array(); 
$i = 0; 
foreach($source as $value) { 

    if($i >= 205) { 
     $i = 0; 
    } 

    if($i == 0) { 
     $result[] = $value; 
    } 

    $i++; 
} 

Oder das gleiche mit Modulo:

$result = array(); 
$i = 0; 
foreach($source as $value) { 
    if($i % 205 == 0) { 
     $result[] = $value; 
    } 
    $i++; 
} 

Diese Methoden können sehr langsam sein, ist es eine Möglichkeit, zu verbessern? Oder spalte ich hier nur Haare?

EDIT

Gute Antworten rundum mit der richtigen Erklärungen, versucht, die passendste als akzeptierte Antwort zu holen. Vielen Dank!

+0

Das sieht vernünftig für mich aus - sind Sie sicher, dass Code Engpässe verursacht? Wenn nicht, profile es, um zu sehen! Wie lange dauert es? –

+0

@Dominic, das ist nicht so sehr ein Flaschenhals, nur ein interessantes Problem, für das ich keine richtige Lösung finden konnte. Glauben Sie nicht, dass eine "richtige" Antwort mehr als einige Millisekunden Ausführungszeit einsparen würde, aber es wäre schön zu wissen. :) –

Antwort

13

Eine foreach-Schleife bietet die schnellste Iteration über Ihr großes Array basierend auf Vergleichstests. Ich würde mit etwas ähnlich bleiben, was Sie haben, wenn jemand das Problem mit Schleife entrolling lösen will.

Diese Antwort sollte schneller laufen.

$result = array(); 
$i = 0; 
foreach($source as $value) { 
    if ($i++ % 205 == 0) { 
     $result[] = $value; 
    } 
} 

Ich habe keine Zeit zu testen, aber Sie könnten eine Variation von @ haim-Lösung, wenn Sie zuerst numerisch Index der Array verwendet werden können. Es ist ein Versuch wert, um zu sehen, wenn Sie irgendwelche Gewinne über meine bisherige Lösung erhalten können:

$result = array(); 
$source = array_values($source); 
$count = count($source); 
for($i = 0; $i < $count; $i += 205) { 
    $result[] = $source[$i]; 
} 

Diese weitgehend würde davon abhängen, wie die Funktion array_values ​​optimiert sind. Es könnte sehr gut funktionieren.

+0

Große Erklärung und tatsächlich ein schneller Weg, um das zu erreichen, was ich versuchte (nur leicht, aber sowieso schneller). Vielen Dank! –

+0

@Tatu, nach einem schnellen Vergleichstest auf array_push und [] auf einem Test-Array von 200.000 Elementen fand ich [] etwa doppelt so schnell zu laufen. Ich habe meine Antwort geändert und empfehle Ihnen, die Aufgabe so zu verwenden, wie Sie sie ursprünglich hatten. –

+1

@Tatu, habe ich schnell die beiden oben genannten Methoden getestet und festgestellt, dass die zweite schneller ist, wenn das Startfeld im Durchschnitt größer als 50.000 Ergebnisse ist. Ich hatte nur Zeit zu testen, beginnend mit numerisch indizierten Arrays, die von array_fill() und range() generiert wurden. –

6

Ich empfehle array_slice

$count = count($array) ; 
for($i=205;$i<$count;$i+=205){ 
    $result[] = array_slice($array,$i,1); 
} 

Wenn Ihr Array numerisch zu verwenden wurde indiziert, wäre dies sehr schnell sein:

$count = count($array) ; 
for($i=205;$i<$count;$i+=205){ 
    $result[] = $array[$i]; 
} 
+1

Zumindest die Syntax ist viel klarer, würde jede Idee tatsächlich die Leistung verbessern? –

+1

For-Schleifen sind langsam. Ihre Zählung() ist auch lächerlich langsam. –

+0

Wenn man die Zählung aus der Schleife herausholt, würde dies tatsächlich wesentlich beschleunigen. –

0

Sie nicht den Array Zeiger bewegen können, so scheint es, mehr als einmal auf einmal. Ich würde dies persönlich verwenden:

reset($source); 
$next = true; 
while($next === true){ 
    $result[] = current($source); 
    for(i=0;i<205;i++){ 
     $next = next($source); 
    } 
} 

Wenn jemand eine Funktion finden kann, dass die Array-Zeiger mehr als nur einen Schritt zu einer Zeit bewegen kann, haben Sie eine bessere Antwort. Ich denke, das ist aber gut.

+0

Sie haben recht, wenn Sie den Mauszeiger um 205 Positionen gleichzeitig bewegen, wäre das die Lösung, sonst sind das alle nur die gleichen Dinge mit unterschiedlicher Syntax, nehme ich an. –

+1

Siehe meine Antwort. ArrayIterator :: seek kann den Zeiger auf die Position – Gordon

+0

verschieben. Was passiert, wenn Werte vorliegen, die nicht als * true * ausgewertet werden? – Gumbo

7

Versuche ArrayIterator::seek()

Auch bessere Ergebnisse eines neues Spl datastructures mit möglicherweise ergeben als einfachen Arrays verwendet wird.

+0

Ich werde das untersuchen, nur der Grund, warum ich das nicht akzeptierte, da die richtige Antwort war, dass ich meine ändern müsste Codebase, um ArrayObjects effizient zu unterstützen. Aber bemerkt und übertrieben. –

1

Wenn dies wirklich ein Flaschenhals ist, sollten Sie vielleicht Ihr Design überdenken, um es numerisch zu indizieren.

EDIT: Oder erstellen und pflegen Sie ein separates Array mit nur der 205th Elemente (die beim Einfügen oder etwas ähnliches aktualisiert wird).

-1

Sie könnten array_keys verwenden, um nur mit den Array-Schlüsseln zu arbeiten.

$keys = array_keys($array); 
for ($i=0, $n=min(count($keys), 130000); $i<$n; $i += 205) { 
    $result[] = $array[$keys[$i]]; 
} 
+3

Wenn das Array-Traversal ein Engpass ist, werden nicht alle Schlüssel abgerufen? – xtofl

+0

@xtofl: Arrays in PHP sind keine echten Arrays wie in anderen Sprachen. Sie werden eher mit einer Hash-Tabelle implementiert. Und die Schlüssel werden separat gespeichert, wahrscheinlich in einer verketteten Liste. – Gumbo

2

Ich denke, die Lösung für dieses Problem liegt nicht in irgendeiner PHP-Syntax, sondern in der Gestaltung Ihres Codes.

Sie könnten entweder das Array numerisch indiziert machen (möglicherweise für Ihre Anwendung nicht plausibel), jedes 205. Element verfolgen oder das Array nur einmal durchsuchen (Cache eine Liste jedes 205. Elements).

In meinen Augen wäre die Verfolgung jedes 205. Artikels einfacher zu implementieren. Sie würden einfach alle Elemente in einer Datenbank zählen oder etwas, und jedes Mal, wenn ein Element hinzugefügt wird, überprüfen Sie den Modulo der Zählung. Wenn Sie ein weiteres 205. Element haben, fügen Sie es zum Array hinzu. Für wenn Elemente gelöscht werden, wäre dies jedoch schwieriger. Möglicherweise müssen Sie das gesamte Array erneut überprüfen, um alle 205 Elemente neu auszurichten.

Dies wäre einfacher, wenn Sie mit dem gelöschten Objekt beginnen und fortfahren könnten, aber das würde auch nur für numerisch indizierte Arrays funktionieren - und wenn das wahr wäre, müssten Sie gar nicht vorwärts gehen, Sie würden nur ein wenig Mathe tun, um es neu zu bestimmen.

  • Numerische Indizes - eine bessere langfristige Lösung, aber härter
  • Die Verfolgung implementieren - einfacher zu implementieren, aber man müsste wieder schmutzig, wenn Sie Elemente
  • Caching Elemente löschen - Sie sollten dies wahrscheinlich auch für die beiden anderen Lösungen tun, aber für sich genommen wäre es schnell, bis das Array geändert wurde. In diesem Fall müssten Sie es wahrscheinlich erneut tun.
0
  • ein zwei dimensionales Array erstellen [205] [N]
  • Laden von Daten in Array
  • Zugang 205. Element für jedes N

albern klingen, aber per Definition ist es am schnellsten da Sie direkt auf Speicherorte zugreifen und keine Vergleiche durchführen.

Verwandte Themen