N ~ 1 den rasgele bir değer olsun ama PHP birkaç belirli değerleri hariç nasıl?

5 Cevap php

rand(1,N) fakat array(a,b,c,..) hariç

Bilmiyorum ya da ben kendim uygulamak zorunda yerleşik bir işlevi var zaten (nasıl?)?

UPDATE

Nitelikli çözelti excluded array boyutunu büyük olup olmadığını altın performans olmalıdır.

5 Cevap

Hiçbir işlevi yerleşik, ancak could bunu:

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

Kafamın üst kapalı, bu yüzden parlatma kullanabilirsiniz - ama en azından varsayımsal olarak bile, sonsuz bir döngü senaryoda sona eremez.

Note: fonksiyon koparsa $exceptions exhausts sizin aralığı - örneğin randWithout(1, 2, array(1,2)) veya randWithout(1, 2, array(0,1,2,3)) (tabii ki) mantıklı bir şey vermeyecektir çağırıyor, ama bu durumda, iade numarası dışında $from olacak - {[(6) }] aralığı, o yakalamak kolaydır böylece.

$exceptions zaten sıralanabilir garanti ise, sort($exceptions); kaldırılabilir.

Eye-candy, Somewhat minimalistic visualisation of the algorithm.

Ben böyle bir fonksiyon yerleşik var sanmıyorum; muhtemelen bunu kendiniz kod gerekecek.

Bu kod, iki çözüm var:

  • Use a loop, to call rand() or mt_rand() until it returns a correct value
    • kötü durumda çağrı rand () birkaç kez, yani
    • ancak N büyük olduğunda, bu Tamam çalışması gerekir, ve birçok yasak değerleri yok.
  • Build an array that contains only legal values
    • Ve kullanmak array_rand ondan bir değer almak için
    • N küçük ise ince çalışacak

Basit yolu ...

<?php

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

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

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

Tam olarak neye ihtiyacınız bağlı ve neden, bu yaklaşım ilginç bir alternatif olabilir.

$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

Her zaman potansiyel sayı kümesi oluşturmak gerekmez, ama bir kez set üretilmesi ve daha sonra aynı kümesinden rasgele sayı bir demet toplama Yani, bu gitmek için iyi bir yol olabilir.

Ne yapmanız gereken size uzunluğu M = N - #of exceptions sürekli bir dizi rastgele bir pozisyon almak ve kolayca geri delikleri ile özgün dizi eşlemek böylece atlanan yerlerde bir dizi hesaplamak olduğunu. Bu zaman ve atlanan diziye eşit alanı gerektirecektir. Ben bu yüzden metinsel yarı psudo kod örneği affet toprağa bir delik php bilmiyorum.

  1. [] İstisnalar dizi olarak aynı uzunlukta Ofset yeni bir dizi yapmak.
  2. Offset [i], orijinal dizideki atlanan i unsurları olurdu hayal olmayan holey dizideki ilk dizin depolamak.
  3. Şimdi rastgele bir eleman almak için. Geri kalan elemanların 0..M sayısında bir rasgele sayı, r seçin.
  4. Offset[i] <= r < Offest[i+i] Bu ikili bir arama ile kolay olduğunu i gibi Bul
  5. Dönüş r + i

Şimdi size diziler uçları ile başa çıkmak için gereken sadece bir kroki ve işler endeksli form 0 veya 1 ve tüm caz eğer. Eğer gerçekten orijinal anında Offset dizi hesaplayabiliriz akıllı iseniz, olsa bu şekilde biraz daha az açıktır.