【问题标题】:How to randomize a PHP array of records, giving more weight to more recent items?如何随机化 PHP 记录数组,为最近的项目赋予更多权重?
【发布时间】:2011-11-13 04:27:44
【问题描述】:

我有一个来自数据库的记录数组(尽管数据库与这个问题无关——它最终变成了一个“行”数组,每一行都是一个数组,其中字符串键对应于字段名称)。例如:

$items = array(
    1 => array('id' => 1, 'name' => 'John', 'created' => '2011-08-14 8:47:39'),
    2 => array('id' => 2, 'name' => 'Mike', 'created' => '2011-08-30 16:00:12'),
    3 => array('id' => 5, 'name' => 'Jane', 'created' => '2011-09-12 2:30:00'),
    4 => array('id' => 7, 'name' => 'Mary', 'created' => '2011-09-14 1:18:40'),
    5 => array('id' => 16, 'name' => 'Steve', 'created' => '2011-09-14 3:10:30'),
    //etc...
);

我想要做的是打乱这个数组,但以某种方式给具有更新“创建”时间戳的项目更多“权重”。随机性不一定是完美的,确切的重量对我来说并不重要。换句话说,如果有一些快速简单的技术对人类来说有点随机,但在数学上不是随机的,我可以接受。此外,如果使用“无限连续”的时间戳不容易做到这一点,我可以将每条记录分配给一天或一周,并根据它们所在的日期或星期进行加权.

一种相对快速/高效的技术更可取,因为这种随机化将发生在我网站中某个页面的每个页面加载时(但如果无法有效地做到这一点,我可以定期运行它并缓存结果) .

【问题讨论】:

    标签: php random weighted


    【解决方案1】:

    您可以使用例如。这个比较函数:

    function cmp($a, $b){
        $share_of_a = $a['id'];
        $share_of_b = $b['id'];
        return rand(0, ($share_of_a+$share_of_b)) > $share_of_a ? 1 : -1;
    }
    

    然后像这样使用它:

    usort($items, 'cmp');
    

    它比较数组的两个元素根据它们的 ID(这更容易,并且它们是根据创建日期分配的 - 较新的元素具有更大的 ID)。比较是随机进行的,每个元素都有不同的成功机会,给新元素更多的机会。 ID 越大(元素越新),它出现在开头的机会就越大

    例如,带有id=16 的元素比元素id=1 出现在结果列表中更早的机会16 倍

    【讨论】:

    • 感谢您的回复。不过,我想我可能已经把你误入歧途了——我的身份证是按最近的顺序排列的,这只是一个巧合。我真的需要基于“创建”日期的重量。不过,我会考虑采用您的算法,看看是否可以在时间戳的整数值而不是 id 上使用它。
    • 其实,我刚刚对此进行了测试,通过 strtotime() 函数将“创建”时间戳转换为 unix 时间戳。问题是它总是以完全相同的顺序返回项目——它没有提供足够的“随机性”。 (我希望每次运行时的顺序都会有所不同)。
    • @JordanLev:如果它返回完全相同的结果,那么您可能做错了什么(至少应该略有不同)。在 Unix 纪元时间戳的情况下,与时间戳本身相比,时间戳之间的差异很小 - 我真的会选择 ID。此外,可能存在一些限制,您可能仅限于使用 32 位整数,因此我建议使用比 Unix 时间戳更小的数字。或者,您可以不使用时间戳,而是使用自数据库中最小时间戳以来的秒数。多次尝试并检查结果是否不同。
    【解决方案2】:

    如何按日期将其拆分为多个块,将每个块随机化,然后将它们重新组合为一个列表?

    【讨论】:

    • 这不够随机——我不希望一天的所有项目都在第二天的所有项目之前。相反,我希望“许多”最近的项目排在第一位,但也有一些最近的项目,等等。
    • 哈哈好吧,按小时或分钟或秒将其分成块也是可能的。
    【解决方案3】:
    //$array is your array
    $mother=array();
    foreach($array as $k->$v) $mother[rand(0,count($array))][$k]=$v;
    ksort($mother);
    $child=array();
    foreach($mother as $ak->$av)
    foreach($av as $k->$v) $child[$k]=$v;
    $array=$child;
    

    或者你可以使用 shuffle()

    【讨论】:

    • 我不明白这如何解决对较新的项目增加“重量”的要求。
    【解决方案4】:

    在受到@Tadeck 回复的部分启发后,我想出了一个解决方案。这有点啰嗦,如果有人可以简化它,那就太好了。但它似乎工作得很好:

    //Determine lowest and highest timestamps
    $first_item = array_slice($items, 0, 1);
    $first_item = $first_item[0];
    $min_ts = strtotime($first_item['created']);
    $max_ts = strtotime($first_item['created']);
    foreach ($items as $item) {
        $ts = strtotime($item['created']);
        if ($ts < $min_ts) {
            $min_ts = $ts;
        }
        if ($ts > $max_ts) {
            $max_ts = $ts;
        }
    }
    
    //bring down the min/max to more reasonable numbers
    $min_rand = 0;
    $max_rand = $max_ts - $min_ts;
    
    //Create an array of weighted random numbers for each item's timestamp
    $weighted_randoms = array();
    foreach ($items as $key => $item) {
        $random_value = mt_rand($min_rand, $max_rand); //use mt_rand for a higher max value (plain old rand() maxes out at 32,767)
        $ts = strtotime($item['created']);
        $ts = $ts - $min_ts; //bring this down just like we did with $min_rand and $max_rand
        $random_value = $random_value + $ts;
        $weighted_randoms[$key] = $random_value;
    }
    
    //Sort by our weighted random value (the array value), with highest first.
    arsort($weighted_randoms, SORT_NUMERIC);
    
    $randomized_items = array();
    foreach ($weighted_randomsas $item_key => $val) {
        $randomized_items[$item_key] = $items[$item_key];
    }
    
    print_r($randomized_items);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-04
      • 1970-01-01
      • 1970-01-01
      • 2018-11-12
      • 1970-01-01
      相关资源
      最近更新 更多