2010-04-23 14 views
5

rand(1,N) ale z wyłączeniem array(a,b,c,..),Jak uzyskać wartość losową od 1 ~ N, ale z wyłączeniem kilku określonych wartości w PHP?

ma już wbudowaną funkcją, która nie wiem czy muszę wdrożyć go samodzielnie (jak?)?

UPDATE

Wykwalifikowana roztwór powinien mieć złoty wydajność czy wielkość excluded array jest duża, czy nie.

+0

Czy Twój 'array wykluczone' może być sortowane? Jeśli tak, możesz usunąć wywołanie funkcji "asort()" w mojej funkcji, co powinno znacznie przyspieszyć działanie. – pinkgothic

+0

Er, 'sort()'. Przepraszam, patrzyłem na to zbyt długo. >.> (Off, aby złapać trochę świeżego powietrza!) – pinkgothic

+0

Re: "Wydaje mi się, że potrzebuję czasu na przetrawienie twojego rozwiązania", wizualizacja tego, co robi mój algorytm: http://pandora.pinkgothic.com/randWithout.png (z wyjątkiem proszę wyobraź sobie, że rand (0,8) czyta rand (1,8), przeskakuję tutaj przez obręcze, aby pobrać obrazy, byłem w pośpiechu. XD) – pinkgothic

Odpowiedz

15

Brak wbudowanego -w funkcji, ale możesz może zrobić:

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

To z mojej głowy, więc mógłbym użyć polerowania - ale przynajmniej nie możesz skończyć w nieskończonej pętli, nawet hipotetycznie.

Uwaga: Przerwy funkcyjne jeśli $exceptionswydechy swoją range - na przykład wywoływanie randWithout(1, 2, array(1,2)) lub randWithout(1, 2, array(0,1,2,3)) nie spowoduje niczego sensownego (oczywiście), ale w takim przypadku zwrócona liczba będzie poza zakresem $from - $to, więc łatwo ją złapać.

Jeśli zagwarantowane jest już sortowanie $exceptions, można usunąć .

Eye-candy: Somewhat minimalistic visualisation of the algorithm.

+0

Nie widzę logiki, która z pewnością wyklucza '$ wyjątki' w twoim kodzie. – Gtker

+0

'if ($ numer> = $ wyjątek)'. Zasadniczo to, co robi kod, polega na zmniejszeniu zakresu liczb do wymaganej ilości (co wymaga tylko jednego uruchomienia przez funkcję), a następnie pomijanie przerw, zgodnie z tym, co definiuje '$ wyjątki '. – pinkgothic

+0

Przykład: 'randWithout (1, 10, array (5, 8))'. '$ number' to' rand (1, 10-2) '==' rand (1, 8) ', powiedzmy, w tym przykładzie, że otrzymujemy' $ number == 7'. Następnie foreach przechodzi przez uporządkowaną tablicę i sprawdza '$ number> = $ exception', która wynosi' 7> = 5' (tak, więc '$ number == 8'), następnie' 8> = 8' (tak, więc '$ number == 9') i wypluwa' 9' na ciebie. – pinkgothic

8

Nie sądzę, że jest wbudowana taka funkcja; prawdopodobnie będziesz musiał sam to zakodować.

Aby ten kod, masz dwa rozwiązania:

  • użyć pętli aby wywołać rand() lub mt_rand() dopóki nie zwróci poprawną wartość
    • co oznacza, nazywając rand() kilka razy, w najgorszym przypadku:
    • , ale powinno to działać poprawnie, jeśli N jest duże i nie masz wielu zabronionych wartości.
  • zbudować tablicę, która zawiera tylko wartości prawnych
    • i używać array_rand wybrać jedną wartość z niej
    • która będzie działać dobrze, jeśli N jest mały
+1

Oba rozwiązania będą wyjątkowo źle działać w określonych sytuacjach. – Gtker

+3

@Runner: Są to proste i proste algorytmy do rozwiązania problemu. Nie ma zbyt wielu (prawdopodobnie Brak) innych sposobów podejścia do problemu: Eliminujesz wykluczone wartości, jeśli je napotkasz (np. 1 powyżej) lub wybierzesz z dozwolonych wartości (alg 2 powyżej). Nawet sprytna implementacja będzie po prostu odmianą tematu podanego powyżej. Metoda, której szukasz, 'Magic()', nie została jeszcze napisana w PHP. – KevenK

+0

Co to jest "złoty występ" i co jest dla ciebie "duże"? – Arkh

4

Najprostszym sposobem ...

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

Tak, jak wiesz, jest to "Użyj pętli, aby wywołać rand() lub mt_rand(), dopóki nie zwróci poprawnej wartości", zasugerował Pascal MARTIN, zastępując "pętlę" przez "rekursję". – pinkgothic

1

Co trzeba zrobić, to obliczyć tablicę pominiętych lokalizacjach, dzięki czemu można wybrać losowo pozycję w ciągłym tablicy długości M = N - #of exceptions i łatwo mapować go z powrotem do oryginalna tablica z dziurami. Będzie to wymagało czasu i przestrzeni równej przeskakiwanej tablicy. Nie znam php z dziury w ziemi, więc wybacz tekstowy kod pseudududo.

  1. Zrób nową tablicę Offset [] taką samą długość jak tablica wyjątków.
  2. w Offset [i] przechowuje pierwszy indeks w wyimaginowanej niewodrębnionej tablicy, która pomijałaby elementy i w oryginalnej tablicy.
  3. Teraz wybierz element losowy. Wybierz liczbę losową, r, w 0..M liczbę pozostałych elementów.
  4. Znajdź i takie, że Offset[i] <= r < Offest[i+i] jest to łatwe przy wyszukiwaniu binarnym
  5. Powrót r + i

teraz, że to tylko szkic trzeba będzie radzić sobie z końców tablic i jeśli rzeczy są indeksowane formularz 0 lub 1 i cały ten jazz. Jeśli jesteś sprytny, możesz obliczyć tablicę Offset w locie z oryginału, jest to jednak nieco mniej jasne.

+1

Zacząłem to, potem dostałem kawę, a potem skończyłem - nigdy nie był to dobry pomysł. Widzę, że pinkgothic ma zasadniczo to samo rozwiązanie, co opisałem, ale oblicza przesunięcia. Zostawię to, jeśli okaże się, że proces jest dla kogoś jasny. – Ukko

+0

To nie jest powszechnie rozpowszechniane, prawda? – Gtker

+0

+1 dla innego rozwiązania jednoprzebiegowego (inne rzeczy mnie przygnębiają), z wyjątkiem braku głosów na dziś. – pinkgothic

7

W zależności od tego, czego dokładnie potrzebujesz i dlaczego, takie podejście może być interesującą alternatywą.

$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 

Tak więc, jeśli nie trzeba generować zbiór potencjalnych numery za każdym razem, ale są generowanie zbioru raz, a następnie zbierając kilka liczb losowych z tego samego zestawu, to może być dobrym sposobem iść.

+0

+1 za korzystanie z wbudowanych funkcji (ale nie mam głosów na dziś, muszę wrócić później) i wykonanie pracy za jednym razem. Zarówno 'shuffle()' jak i 'array_rand()' połączone mogą być przesadą (lub może nie być wystarczającą, zależą od kontekstu), ale zdecydowanie wykonują zadanie! – pinkgothic

+0

Występ będzie straszny, gdy 'N' będzie ogromny. @ Pinkgothic, wydaje mi się, że potrzebuję trochę czasu na przetrawienie twojego rozwiązania :) – Gtker

+0

@pinkgothic :) Dzięki! Tak, nie sugeruję wykonywania zarówno shuffle(), jak i array_rand(), tylko jednego lub drugiego, w zależności od tego, jakie liczby są potrzebne i jaki rodzaj wyników może działać w przypadku problemu. @Runner Tak, to nie będzie genialne dla ogromnego N :) Ale pomyślałem, że może to być opłacalna odpowiedź, jeśli chcesz tylko raz wygenerować zestaw liczb, a następnie generować losowe odpowiedzi wiele razy. I w zależności od ogromnego $ :) Odpowiedź jest nieco równoważenia w zależności od dokładnego pytania, jeśli chodzi o wydajność ... –

0

Może jest za późno na odpowiedź, ale znalazłem ten kawałek kodu gdzieś w mojej głowie, próbując uzyskać losowe dane z bazy danych na podstawie losowego identyfikatora z wyłączeniem pewnej liczby.

$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

Dzięki za wniesienie wkładu! :) Chociaż powinieneś wiedzieć, że jest to w zasadzie wariant odpowiedzi Pascala MARTIN'a i sasa.Hipotetycznie (ale z pozoru mało prawdopodobne), rozwiązania te mogą znaleźć się na infiniteloop, lub w bardziej łagodnym przypadku zajmie to dużo czasu, jeśli '$ excludedData' jest duże, czego Gtker wydaje się chcieć uniknąć. – pinkgothic

0

To najszybciej & najlepiej wydajność sposób to zrobić:

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