2010-04-23 2 views
5

rand(1,N) aber ohne array(a,b,c,..),Wie erhält man einen zufälligen Wert von 1 ~ N, aber mehrere spezifische Werte in PHP ausgeschlossen?

ist es bereits eine eingebaute Funktion, die ich weiß nicht, oder muss ich es selbst implementieren (wie?)?

UPDATE

Die qualifizierte Lösung sollte Gold Leistung haben, ob die Größe des excluded array groß ist oder nicht.

+0

Ist Ihr 'ausgeschlossen Array' sortiert werden wahrscheinlich? Wenn das der Fall ist, können Sie den Aufruf 'asort()' in meiner Funktion entfernen, was die Dinge erheblich beschleunigen sollte. – pinkgothic

+0

Er, 'sort()'. Entschuldige, ich habe das schon zu lange angeguckt. >.> (Aus, um frische Luft zu schnappen!) – pinkgothic

+0

Re: "scheint, ich brauche etwas Zeit, um Ihre Lösung zu verdauen", haben Sie eine Visualisierung dessen, was mein Algorithmus tut: http://pandora.pinkgothic.com/randWithout.png (außer bitte stell dir vor, der rand (0,8) liest rand (1,8), ich spring durch hoops hier um bilder hochgeladen zu bekommen, ich war in eile XD) – pinkgothic

Antwort

15

Nein gebaut -in Funktion, aber Sie könnten tun dies:

function randWithout($from, $to, array $exceptions) { 
    sort($exceptions); // lets us use break; in the foreach reliably 
    $number = rand($from, $to - count($exceptions)); // or mt_rand() 
    foreach ($exceptions as $exception) { 
     if ($number >= $exception) { 
      $number++; // make up for the gap 
     } else /*if ($number < $exception)*/ { 
      break; 
     } 
    } 
    return $number; 
} 

Das ist ab von meinem Kopf, so könnte es Polieren verwenden - aber zumindest kann man nicht in einem Endlos-Loop-Szenario enden, auch nur hypothetisch.

Hinweis: Die Funktion bricht, wenn $exceptionserschöpft Ihr Bereich - zum Beispiel Rufen randWithout(1, 2, array(1,2)) oder randWithout(1, 2, array(0,1,2,3)) wird nichts vernünftig ergeben (offensichtlich), aber in diesem Fall wird die zurückgegebene Nummer außerhalb der $from - $to reichen, so dass es leicht zu fangen ist.

Wenn $exceptions garantiert bereits sortiert ist, kann sort($exceptions); entfernt werden.

Augenschmaus: Somewhat minimalistic visualisation of the algorithm.

+0

Ich sehe nicht die Logik, die '$ Ausnahmen 'in Ihrem Code sicher ausschließt. – Gtker

+0

'if ($ Nummer> = $ Ausnahme)'. Im Wesentlichen reduziert der Code den Bereich der Zahlen auf die tatsächlich benötigte Menge (und benötigt somit nur einen Durchlauf durch die Funktion) und überspringt dann die Lücken gemäß der Definition von $ exceptions. – pinkgothic

+0

Beispiel: 'randWithout (1, 10, Array (5, 8))'. '$ number' ist' rand (1, 10-2) '==' rand (1, 8) ', also sagen wir in diesem Beispiel, dass wir' $ number == 7' erhalten. Dann läuft die foreach durch das geordnete Array und überprüft '$ number> = $ exception', was' 7> = 5' ist (ja, also '$ number == 8'), dann' 8> = 8' (ja, also '$ number == 9'), und spuckt' 9' bei dir aus. – pinkgothic

8

Ich glaube nicht, dass eine solche Funktion eingebaut ist; Sie werden es wahrscheinlich selbst programmieren müssen.

Um dies zu kodieren, haben Sie zwei Möglichkeiten:

  • Verwenden Sie eine Schleife, rand() aufrufen oder mt_rand(), bis er einen korrekten Wert
    • gibt die Aufruf rand bedeutet() mehrere Zeiten, im schlimmsten Fall
    • aber das sollte OK funktionieren, wenn N groß ist, und Sie nicht viele verbotene Werte haben.
  • ein Array aufzubauen, die nur rechtliche Werte
    • Und verwenden array_rand enthält einen Wert zur Auswahl es
    • die fein wird funktionieren, wenn N klein ist
+1

Beide Lösungen haben in bestimmten Situationen eine extrem schlechte Leistung. – Gtker

+3

@Runner: Dies sind sowohl einfache als auch einfache Algorithmen, um das Problem zu lösen. Es gibt nicht zu viele (wahrscheinlich keine) andere Möglichkeiten, das Problem anzugehen: Sie eliminieren entweder die ausgeschlossenen Werte, wenn Sie ihnen begegnen (alg 1 oben) oder Sie wählen aus den erlaubten Werten (alg 2 oben). Selbst eine clevere Implementierung wird nur eine Variation zu einem oben genannten Thema sein. Die Methode, nach der Sie suchen, 'Magic()', wurde noch nicht in PHP geschrieben. – KevenK

+0

Was ist "Gold Performance" und was ist "groß" für dich? – Arkh

4

Die einfachste Art und Weise ...

<?php 

function rand_except($min, $max, $excepting = array()) { 

    $num = mt_rand($min, $max); 

    return in_array($num, $excepting) ? rand_except($min, $max, $excepting) : $num; 
} 
?> 
+1

Nur damit Sie wissen, das ist die "Verwenden Sie eine Schleife, Rand() oder mt_rand() aufrufen, bis es einen korrekten Wert" Pascal MARTIN vorgeschlagen, ersetzt "Schleife" mit "Rekursion" zurückgibt. – pinkgothic

1

Was Sie tun müssen, ist eine Reihe von übersprungenen Stellen zu berechnen, so dass Sie eine zufällige Position in einem kontinuierlichen Array der Länge M = N - #of exceptions und es leicht Karte zurück zur pflücken Original-Array mit Löchern. Dies erfordert Zeit und Speicherplatz, die dem übersprungenen Array entsprechen. Ich kenne PHP nicht von einem Loch in der Erde, also verzeihen Sie den Text Semipsudokode Beispiel.

  1. Erstellen Sie ein neues Array Offset [] die gleiche Länge wie das Array Exceptions.
  2. in Offset [i] speichern den ersten Index in der imaginären nicht-holey Array, die i Elemente im ursprünglichen Array übersprungen hätte.
  3. Jetzt ein zufälliges Element auswählen. Wählen Sie eine Zufallszahl, r, in 0..M die Anzahl der verbleibenden Elemente.
  4. Finden i so dass Offset[i] <= r < Offest[i+i] dies mit einer binären Suche einfach
  5. Return r + i

Nun, das ist nur eine Skizze, Sie müssen sich mit den Enden der Arrays behandeln und wenn die Dinge indiziert Form 0 oder 1 und all dieser Jazz. Wenn Sie schlau sind, können Sie das Offset-Array tatsächlich vom Original aus berechnen, es ist jedoch etwas weniger klar.

+1

Ich begann das, dann bekam Kaffee, dann fertig - nie eine gute Idee. Ich sehe, dass Pinkgothic im Grunde die gleiche Lösung hat wie ich beschrieben habe, aber sie berechnet die Offsets. Ich werde das lassen, wenn es den Prozess für jemanden klar macht. – Ukko

+0

Es ist nicht allgemein verbreitet, oder? – Gtker

+0

+1 für eine andere Single-Pass-Lösung (das andere Zeug bedrückt mich), außer ich habe keine Stimmen für heute. – pinkgothic

7

Je nachdem, was genau Sie benötigen und warum, könnte dieser Ansatz eine interessante Alternative sein.

$numbers = array_diff(range(1, N), array(a, b, c)); 
// Either (not a real answer, but could be useful, depending on your circumstances) 
shuffle($numbers); // $numbers is now a randomly-sorted array containing all the numbers that interest you 
// Or: 
$x = $numbers[array_rand($numbers)]; // $x is now a random number selected from the set of numbers you're interested in 

Also, wenn Sie den Satz von möglichen Zahlen nicht jedes Mal neu erzeugen müssen, aber die Menge einmal generieren und dann eine Reihe von Zufallszahl aus dem gleichen Satz Kommissionierung, könnte dies ein guter Weg, gehen.

+0

+1 für die Verwendung von integrierten Funktionen (aber ich habe keine Stimmen für heute, muss später wiederkommen) und die Arbeit in einem Durchgang erledigen. Sowohl shuffle() als auch 'array_rand()' kombiniert können zu viel sein (oder sind vielleicht nicht genug, abhängig vom Kontext), aber es wird definitiv erledigt! – pinkgothic

+0

Die Leistung wird schrecklich sein, wenn 'N' riesig ist. @ Pinkgothic, scheint ich brauche etwas Zeit, um Ihre Lösung zu verdauen :) – Gtker

+0

@pinkgothic :) Danke! Yup, ich schlage nicht vor, sowohl shuffle() als auch array_rand() zu machen, nur eines oder das andere, abhängig davon, warum die Zahlen benötigt werden und welche Art von Ergebnissen für das Problem funktionieren könnte. @Runner Ja, es wird nicht für eine große N brilliant sein :) Aber ich dachte, es könnte eine praktikable Antwort sein, wenn Sie nur die Nummer einmal generieren möchten, und dann zufällige Antworten mehrere Male generieren. Und abhängig von $ riesengroß :) Die Antwort ist ein bisschen wie ein Balanceakt abhängig von der genauen Frage, wenn es um die Leistung geht ... –

0

Vielleicht ist es zu spät für die Antwort, aber ich fand dieses Stück Code irgendwo in meinen Gedanken beim Versuch, zufällige Daten von der Datenbank basierend auf zufälligen ID mit Ausnahme einiger Zahlen zu bekommen.

$excludedData = array(); // This is your excluded number 
 
$maxVal = $this->db->count_all_results("game_pertanyaan"); // Get the maximum number based on my database 
 

 
$randomNum = rand(1, $maxVal); // Make first initiation, I think you can put this directly in the while > in_array paramater, seems working as well, it's up to you 
 
while (in_array($randomNum, $excludedData)) { 
 
    $randomNum = rand(1, $maxVal); 
 
} 
 

 
$randomNum; //Your random number excluding some number you choose

+0

Danke für Ihren Beitrag! :) Obwohl Sie wissen sollten, dass dies im Grunde eine Variante von Pascal MARTIN und Sasa Antworten ist.Hypothetisch (aber verschwindend unwahrscheinlich) können diese Lösungen in einem Infiniteloop enden, oder im gutartigen Fall dauert es lange, wenn '$ excludedData 'groß ist, was Gtker anscheinend vermeiden möchte. – pinkgothic

0

Dies ist die schnellste & beste Leistung Art und Weise, es zu tun:

$all = range($Min,$Max); 
$diff = array_diff($all,$Exclude); 
shuffle($diff); 
$data = array_slice($diff,0,$quantity);