2010-04-23 9 views
5

rand(1,N) esclusi array(a,b,c,..),Come ottenere un valore casuale da 1 ~ N ma escludendo diversi valori specifici in PHP?

c'è già una funzione built-in che non so o devo per la sua attuazione me stesso (come?)?

UPDATE

La soluzione qualificato dovrebbe avere prestazioni d'oro se la dimensione del excluded array è grande o no.

+0

È il vostro 'allineamento esclusa' probabile che essere ordinati? In tal caso è possibile rimuovere la chiamata 'asort()' nella mia funzione, che dovrebbe accelerare notevolmente le cose. – pinkgothic

+0

Er, 'sort()'. Scusa, lo sto fissando da troppo tempo. >. (Off per prendere un po 'di aria fresca!) – pinkgothic

+0

Ri: "sembra che mi serva del tempo per digerire la tua soluzione", avere una visualizzazione di ciò che fa il mio algoritmo: http://pandora.pinkgothic.com/randWithout.png (eccetto per favore immagina che il rand (0,8) legge rand (1,8), sto saltando i cerchi qui per ottenere immagini caricate, ero di fretta XD) – pinkgothic

risposta

15

No costruito -in funzione, ma è possibile fare questo:

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; 
} 

Questo è fuori dalla mia testa, quindi potrebbe usare la lucidatura - ma almeno non si può finire in uno scenario a ciclo infinito, anche ipoteticamente.

Nota: Le interruzioni di funzione se $exceptionsscarichi vostra gamma - ad esempio chiamare randWithout(1, 2, array(1,2)) o randWithout(1, 2, array(0,1,2,3)) non darà alcun risultato ragionevole (ovviamente), ma in tal caso, il numero restituito sarà al di fuori della gamma $from - $to, quindi è facile da catturare.

Se è già possibile ordinare $exceptions, è possibile rimuovere sort($exceptions);.

Eye-candy: Somewhat minimalistic visualisation of the algorithm.

+0

Non vedo la logica che certamente escluderà '$ eccezioni' nel codice. – Gtker

+0

'if ($ numero> = $ eccezione)'. In sostanza ciò che sta facendo il codice è ridurre l'intervallo di numeri alla quantità effettivamente necessaria (richiedendo quindi solo una corsa attraverso la funzione), quindi saltare gli intervalli come da definizione di $ $ eccezioni. – pinkgothic

+0

Esempio: 'randWithout (1, 10, array (5, 8))'. '$ numero' è' rand (1, 10-2) '==' rand (1, 8) ', quindi, diciamo, in questo esempio, otteniamo' $ numero == 7'. Quindi il foreach attraversa l'array ordinato e controlla '$ number> = $ exception', che è' 7> = 5' (sì, quindi '$ number == 8'), quindi' 8> = 8' (sì, quindi '$ number == 9'), e sputa' 9' contro di te. – pinkgothic

8

Non penso che ci sia una funzione incorporata di questo tipo; probabilmente dovrai codificarlo da solo.

Per codificare questo, ci sono due soluzioni:

  • utilizzare un ciclo, da chiamare rand() o mt_rand() fino a quando non restituisce un valore corretto
    • che significa chiamare rand() diversi volte, nel peggiore dei casi
    • ma questo dovrebbe funzionare bene se N è grande, e non si hanno molti valori proibiti.
  • costruire un array che contiene solo i valori giuridici
    • e utilizzare array_rand per scegliere un valore da esso
    • che funzionerà bene se N è piccolo
+1

Entrambe le soluzioni avranno prestazioni estremamente negative in determinate situazioni. – Gtker

+3

@ Runner: questi sono algoritmi semplici e semplici per risolvere il problema. Non ci sono troppi (probabilmente Nessuno) altri modi per affrontare il problema: o elimini i valori esclusi se li incontri (vedi sopra 1) o selezioni dai valori consentiti (alg 2 sopra). Anche un'implementazione intelligente sarà solo una variazione su un tema sopra riportato. Il metodo che stai cercando, 'Magic()', non è ancora stato scritto in PHP. – KevenK

+0

Che cos'è "prestazione d'oro" e cosa è "grande" per te? – Arkh

4

Il modo più semplice ...

<?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

Solo così sai, questo è "Usa un ciclo, per chiamare rand() o mt_rand() finché non restituisce un valore corretto" suggerito da Pascal MARTIN, sostituendo "loop" con "ricorsione". – pinkgothic

1

Quello che dovete fare è calcolare una serie di posizioni saltate modo da poter scegliere una posizione casuale in una matrice continua di lunghezza M = N - #of exceptions e facilmente mappare nuovamente al matrice originale con fori. Ciò richiederà tempo e spazio pari alla matrice saltata. Non so php da un buco nel terreno, quindi perdona l'esempio di codice semi-psudo testuale.

  1. Crea un nuovo array Offset [] della stessa lunghezza della matrice Exceptions.
  2. in Offset [i] memorizza il primo indice nell'array immaginato senza buche che avrebbe saltato gli elementi i nell'array originale.
  3. Ora scegliere un elemento casuale. Selezionare un numero casuale, r, in 0..M il numero di elementi rimanenti.
  4. Trova i tale che Offset[i] <= r < Offest[i+i] questo è facile con una ricerca binaria
  5. ritorno r + i

Ora, che è solo uno schizzo è necessario affrontare le estremità delle matrici e se le cose sono indicizzati forma 0 o 1 e tutto quel jazz. Se sei intelligente, puoi effettivamente calcolare l'array Offset in tempo reale dall'originale, tuttavia è un po 'meno chiaro.

+1

Ho iniziato questo, poi ho preso il caffè, quindi ho finito - mai una buona idea. Vedo che pinkgothic ha praticamente la stessa soluzione che ho descritto, ma sta calcolando gli offset. Lascerò questo in caso rende il processo chiaro per qualcuno. – Ukko

+0

Non è universalmente distribuito, vero? – Gtker

+0

+1 per un'altra soluzione single-pass (l'altra roba mi deprime), tranne che per oggi non ho più voti. – pinkgothic

7

A seconda esattamente di ciò che è necessario e perché, questo approccio potrebbe essere un'alternativa interessante.

$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 

Quindi, se non è necessario per generare la serie di potenziali numeri ogni volta, ma sta generando il set una volta e poi scegliere un po 'di numeri casuali dallo stesso insieme, questo potrebbe essere un buon modo andare.

+0

+1 per l'utilizzo di funzioni integrate (ma ho esaurito i voti per oggi, è necessario tornare più tardi) e portare a termine il lavoro in un'unica soluzione. Sia 'shuffle()' che 'array_rand()' combinati potrebbero essere overkill (o potrebbero non essere sufficienti, dipende dal contesto), ma sicuramente completano il lavoro! – pinkgothic

+0

La performance sarà terribile quando 'N' è enorme. @ Pinkgothic, sembra che mi serva del tempo per digerire la tua soluzione :) – Gtker

+0

@pinkgothic :) Grazie! Sì, non sto suggerendo di fare sia shuffle() che array_rand(), solo uno o l'altro, a seconda del motivo per cui i numeri sono necessari, e che tipo di risultati potrebbero funzionare per il problema. @Runner Sì, non sarà brillante per un enorme N :) Ma ho pensato che potrebbe essere una risposta valida se si desidera generare il numero impostato una sola volta, e quindi generare risposte casuali più volte. E a seconda di $ enorme :) La risposta è un po 'un atto di bilanciamento a seconda della domanda esatta, quando si tratta di prestazioni ... –

0

Forse è troppo tardi per rispondere, ma ho trovato questo pezzo di codice da qualche parte nella mia mente quando ho cercato di ottenere dati casuali dal Database in base a un ID casuale escludendo un certo numero.

$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

Grazie per aver contribuito! :) Anche se dovresti sapere che questa è fondamentalmente una variante delle risposte di Pascal MARTIN e sasa.Ipoteticamente (ma incredibilmente improbabile), queste soluzioni possono finire in un infiniteloop, o nel caso più benevolo richiedere molto tempo se '$ excludedData' è grande, che è il genere di cose che Gtker sembra voler evitare. – pinkgothic

0

Questo è il più veloce & migliori prestazioni modo per farlo:

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