MySQL: Bazı Girişler Doğru Rastgele Girişi, ama Ağırlık Seçin

7 Cevap php

Bunu düzeltmek benim geliştiriciler için beklemekten sıkıldım, bu yüzden ben sizi istemeye karar verdi. Kurulum anlamda bir sürü yapmaz eğer ben zaten çok üzgünüm, beni verdiklerinin değiştirmeye çalışıyorum. Herhalde bunu değiştirmek ama mümkün olduğu kadar mevcut tablo kurulum kadar tutmak istiyor olabilir.

Ben bu kayıtları bir grup, ve adında bir sütun ile bir MySQL tablo var "Çarpan." Bu sütun için varsayılan (ve en yaygın) değeri 0, ancak herhangi bir sayı olabilir.

Ne yapmam gerekiyor rastgele bu tabloya tek bir girdiyi seçin olduğunu. Ancak, satırlar "Çarpan" sütununda sayısına göre ağırlıklandırılmış. 0 değeri hiç de ağırlıklı değil demektir. 1 değeri giriş iki tabloda sanki, iki katı kadar ağırlıklı oluyor demektir. 2 Değeri giriş tablosunda üç kez sanki, üç kat daha fazla ağırlıklı oluyor demektir.

Ben SELECT ve RAND () ile bunu nasıl anlamaya çalışıyorum, ama ağırlık nasıl bilmiyorum. Bu mümkün mü?

7 Cevap

Ben bu eski olduğunu biliyorum, ama umarım bu Google'cuların yardımcı olacaktır.

This guy aynı soruyu sorar. O Frank olarak aynı diyor, ama benim test hemen hemen mükemmel sonuç verdi, ağırlıklar hakkını dışarı gelmiyor ve yorumların birisi ORDER BY -LOG(1.0 – RAND()) / Multiplier kullanarak önerir.

(Orada herhangi matematikçiler bu doğru neden açıklamak istiyorsan, beni aydınlatmak! Ama çalışıyor lütfen)

Dezavantajı sıfıra bölünmesi sona ereceğini gibi, geçici bir seçeneği devre dışı bırakmak için 0 ağırlık ayarlamak olamazdı olurdu. Ama her zaman bir WHERE Multiplier > 0 ile filtre olabilir.

0, 1 ve 2 ama 1, 2 ve 3 tuşlarını kullanın etmeyin Sonra bir çarpanı olarak bu değeri kullanabilirsiniz.:

SELECT * FROM tablename ORDER BY (RAND() * Multiplier);

Eh, ben PHP ağırlıkları mantığı koymak istiyorum:

<?php
    $weight_array = array(0, 1, 1, 2, 2, 2);
    $multiplier = $weight_array[array_rand($weight_array)];
?>

ve sorgu:

SELECT *
FROM `table`
WHERE Multiplier = $multiplier
ORDER BY RAND()
LIMIT 1

Ben çalışacağını düşünüyorum :)

Bu konuyu Googling diğerleri için, ben de böyle bir şey yapabileceğine inanıyorum:

SELECT strategy_id
FROM weighted_strategies AS t1 
WHERE (
   SELECT SUM(weight) 
   FROM weighted_strategies AS t2 
   WHERE t2.strategy_id<=t1.strategy_id
)>@RAND AND 
weight>0
LIMIT 1

Tüm kayıtlar için ağırlıkların toplamı must n-1, ve @ RAND 0 ve n-1 dahil arasında rastgele bir değer olmalıdır.

@ RAND SQL ayarlamak veya arama kodu bir tamsayı değeri olarak eklenebilir.

Subselect bu verilen rastgele değerini aştığı onu kontrol, tüm ödemeler dengesi Records'un ağırlıkları özetlemek olacaktır.

<?php
/**
 * Demonstration of weighted random selection of MySQL database.
 */
$conn = mysql_connect('localhost', 'root', '');

// prepare table and data.
mysql_select_db('test', $conn);
mysql_query("drop table if exists temp_wrs", $conn);
mysql_query("create table temp_wrs (
    id int not null auto_increment,
    val varchar(16),
    weight tinyint,
    upto smallint,
    primary key (id)
)", $conn);
$base_data = array(    // value-weight pair array.
    'A' => 5,
    'B' => 3,
    'C' => 2,
    'D' => 7,
    'E' => 6,
    'F' => 3,
    'G' => 5,
    'H' => 4
);
foreach($base_data as $val => $weight) {
    mysql_query("insert into temp_wrs (val, weight) values ('".$val."', ".$weight.")", $conn);
}

// calculate the sum of weight.
$rs = mysql_query('select sum(weight) as s from temp_wrs', $conn);
$row = mysql_fetch_assoc($rs);
$sum = $row['s'];
mysql_free_result($rs);

// update range based on their weight.
// each "upto" columns will set by sub-sum of weight.
mysql_query("update temp_wrs a, (
    select id, (select sum(weight) from temp_wrs where id <= i.id) as subsum from temp_wrs i 
) b
set a.upto = b.subsum
where a.id = b.id", $conn);

$result = array();
foreach($base_data as $val => $weight) {
    $result[$val] = 0;
}
// do weighted random select ($sum * $times) times.
$times = 100;
$loop_count = $sum * $times;
for($i = 0; $i < $loop_count; $i++) {
    $rand = rand(0, $sum-1);
    // select the row which $rand pointing.
    $rs = mysql_query('select * from temp_wrs where upto > '.$rand.' order by id limit 1', $conn);
    $row = mysql_fetch_assoc($rs);
    $result[$row['val']] += 1;
    mysql_free_result($rs);
}

// clean up.
mysql_query("drop table if exists temp_wrs");
mysql_close($conn);
?>
<table>
    <thead>
        <th>DATA</th>
        <th>WEIGHT</th>
        <th>ACTUALLY SELECTED<br />BY <?php echo $loop_count; ?> TIMES</th>
    </thead>
    <tbody>
    <?php foreach($base_data as $val => $weight) : ?>
        <tr>
            <th><?php echo $val; ?></th>
            <td><?php echo $weight; ?></td>
            <td><?php echo $result[$val]; ?></td>
        </tr>
    <?php endforeach; ?>
    <tbody>
</table>

Eğer N satırları seçmek istiyorsanız ...

  1. toplamını yeniden hesaplamak.
  2. aralığı ("kadar" sütunu) sıfırlayın.
  3. satırı seçin ki $rand işaretleme.

önce seçilen satırlar her seçim döngü ekarte edilmelidir. where ... id not in (3, 5);

Whatever you do, it is giong to be terrible because it will involve: * Getting the total "weights" for all columns as ONE number (including applying the multiplier). * Getting a random number between 0 and that total. * Getting all entries and runing them along, deducting the weight from the random number and choosing the one entry when you run out of items.

Ortalama olarak yarım masa boyunca çalışacaktır. Performans - tablo küçük olmadıkça, daha sonra bellekte mySQL dışında bunu - YAVAŞ olacaktır.

Pseudo-kod sonucu (rand(1, num) % rand(1, num)) num doğru daha doğru 0 ve daha az alacak. Tersini almak için num sonucunu çıkarma.

Benim uygulama dili PHP Yani, bu gibi görünmelidir:

$arr = mysql_fetch_array(mysql_query(
    'SELECT MAX(`Multiplier`) AS `max_mul` FROM tbl'
));
$MaxMul = $arr['max_mul']; // Holds the maximum value of the Multiplier column

$mul = $MaxMul - ( rand(1, $MaxMul) % rand(1, $MaxMul) );

mysql_query("SELECT * FROM tbl WHERE Multiplier=$mul ORDER BY RAND() LIMIT 1");

Yukarıdaki kod açıklaması:

  1. Çarpan sütunundaki en yüksek değeri getir
  2. rastgele bir Çarpan değeri hesaplamak (Çarpan sütunundaki maksimum değere doğru ağırlıklı)
  3. Bu Çarpan değeri olan rastgele satır getir

Ayrıca sadece MySQL kullanarak ulaşılabilir bulunuyor.

Proving that the pseudo-code (rand(1, num) % rand(1, num)) will weight toward 0: Execute the following PHP code to see why (in this example, 16 is the highest number):

$v = array();

for($i=1; $i<=16; ++$i)
    for($k=1; $k<=16; ++$k)
        isset($v[$i % $k]) ? ++$v[$i % $k] : ($v[$i % $k] = 1);

foreach($v as $num => $times)
        echo '<div style="margin-left:', $times  ,'px">
              times: ',$times,' @ num = ', $num ,'</div>';