广告系统提示

Advertisement System Tips

我正在创建一个广告系统,它可以更频繁地显示出价最高者的广告。

这是我正在使用的 table 结构的示例,但已简化...

+----+----------+------------------------+----------------------+-----+
| id | name     | image                  | destination          | bid |
+----+----------+------------------------+----------------------+-----+
| 1  | abc, co  | htt.../blah            | htt...djkd.com/      | 3   |
+----+----------+------------------------+----------------------+-----+
| 2  | facebook | htt.../blah            | htt...djkd.com/      | 200 |
+----+----------+------------------------+----------------------+-----+
| 3  | google   | htt.../blah            | htt...djkd.com/      | 78  |
+----+----------+------------------------+----------------------+-----+

现在,我正在从数据库中选择值,然后将它们插入到一个数组中,然后随机挑选一个类似于以下内容:

$ads_array = [];
$ads = Ad::where("active", "=", 1)->orderBy("price", "DESC");

if ($ads->count() > 0) {
    $current = 0;
    foreach ($ads->get() as $ad) {
        for ($i = 0; $i <= ($ad->price == 0 ? 1 : $ad->price); $i++) {
            $ads_array[$current] = $ad->id;
            $current++;
        }
    }

    $random = rand(0,$current-1);
    $ad = Ad::where("id", "=", $ads_array[$random])->first();

    ...
}

所以,本质上这是在将广告的 ID 插入数组 1*$bid 次。遗憾的是,这是非常低效的(出于显而易见的原因),但这是我能想到的最好的方法。

有没有更好的方法从我的数据库中随机挑选广告?同时仍然让出价更高的人更有可能被展示?

看起来这可能会奏效(但所有功劳都归功于此 guy in the comments

SELECT ads.*
FROM ads
ORDER BY -log(1.0 - rand()) / ads.bid
LIMIT 1

测试这个的脚本:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;', 'test', 'test');


$times = array();

// repeat a lot to have real values
for ($i = 0; $i < 10000; $i++) {
    $stmt = $pdo->query('SELECT ads.* FROM ads ORDER BY -log(1.0 - rand()) / bid LIMIT 1');
    $bid = $stmt->fetch()['bid'];
    if (isset($times[$bid])) {
        $times[$bid] += 1;
    } else {
        $times[$bid] = 1;
    }
}

// echoes the number of times one bid is represented
var_dump($times);

我从那个测试中得出的数字非常好:

// key is the bid, value is the number of times this bid is represented
array (size=3)
  200 => int 7106
  78 => int 2772
  3 => int 122

进一步参考数学解释

Many important univariate distributions can be sampled by inversion using simple closed form expressions. Some of the most useful ones are listed here.

Example 4.1 (Exponential distribution). The standard exponential distribution has density f(x) = e−x on x > 0. If X has this distribution, then E(X) = 1, and we write X ∼ Exp(1). The cumulative distribution function is F(x) = P(X x) = 1 − e−x, with F−1(u) = −log(1 − u). Therefore taking X = − log(1 − U ) for U ∼ U(0, 1), generates standard exponential random variables. Complementary inversion uses X = − log(U ).

The exponential distribution with rate λ > 0 (and mean θ = 1/λ) has PDF λexp(−λx) for 0 x < ∞. If X has this distribution, then we write X ∼ Exp(1)/λ or equivalently X ∼ θExp(1), depending on whether the problem is more naturally formulated in terms of the rate λ or mean θ. We may generate X by taking X = − log(1 − U )/λ.

来自http://statweb.stanford.edu/~owen/mc/Ch-nonunifrng.pdf