如何制定受控的“洗牌”订单?

时间:2010-07-22 23:00:42

标签: php javascript mysql algorithm shuffle

我在sql数据库中有一套测验游戏问题(实际上是javascript和sqlite)。这些问题都有难度级别从1到5,5最难。以下是数据的简化可视化......


+---------+--------------+  
|   id    | difficulty   |   
+---------+--------------+  
| 1       |      1       |    
| 2       |      5       |    
| 3       |      2       |    
| 4       |      3       |    
| 5       |      2       | 
| 6       |      2       |    
| 7       |      4       |    
| 8       |      1       |    
| 9       |      5       |    
| 10      |      3       |      
+---------+--------------+   

现在我可以在sql或代码中随机播放这些内容,这样它们就是一个没有重复的随机顺序,但我也想控制难度字段的排序方式。

因此,例如,我可以有一个改组的问题,其中难度级别顺序看起来像这样......

1,1,5,2,3,3,2,2,2,4

这有几个“困难”,这不是我想要的。玩游戏的用户将得到几组同样困难的问题。像这样的订单会更好......

1,2,3,2,5,4,1,2,3,2

我想确保问题被洗牌,但没有困难聚集。如果有任何“团块”,那么很少有困难。对MySQL / javascript(或PHP)的任何帮助都会很棒。

5 个答案:

答案 0 :(得分:5)

不是将所有ID组合在一起,而是为什么不按难度对它们进行分组,然后将每个部分随机化,然后逐个将它们拉出来。或者,一旦它们被随机分类,您可以从随机难度中拉出它们,然后移除该难度级别,直到您有一个问题。

这是我在回答sje397时所考虑的问题,所以我会将其添加到我的答案中。

只要所有其他选择加起来最大的组减1,你就没有结块(假设你的算法是正确的)。然而,该算法基本上采取从A(具有最多选择数的组)中挑选的形式,从另一组中挑选,从A等中挑选,直到A等于其他组的大小。所以最好的算法会检查找到最大的组并从中挑选。然后它将从另一个组中选择,然后检查哪个组是最大的组,然后从中选择,除非它是之前选择的组等。

答案 1 :(得分:2)

以下策略在代码中如何:(以下是项目符号列表,但我无法在项目符号列表正确显示之后显示代码 - 我彻底厌恶本网站使用的“降价”垃圾)

难以订购问题

将问题分成两半,一个“简单”列表和一个“硬”列表

从简单和硬列表中逐一提问,在两者之间交替。 (这意味着在问题序列中你会有一个轻微到困难的小趋势,你可能会或可能不会这样。)

原始实施:

$resultset = your_preferred_query_function('SELECT id FROM question ORDER BY difficulty');
$questions_temp = array();
while ( $row = mysqli_fetch_assoc() ) {
    $questions_temp[] = $row['id'];
}
if ( count($questions) % 2 ) {
    $loop_limit = (count($questions) - 1) / 2;
    $halfway = (count($questions) + 1) / 2;
    $questions[0] = $questions_temp[$loop_limit];
} else {
    $loop_limit = count($questions) / 2;
    $halfway = count($questions) / 2;
    $questions = array();
}
for ($i=0; $i<$loop_limit; $i++) {
    $questions[] = $questions_temp[$i];
    $questions[] = $questions_temp[$halfway+$i];
}

现在$questions是一个数组,其中包含按照我的建议订购的问题。

答案 2 :(得分:1)

在一个真正随机的样本中,“clumps”自然会出现。因此,如果您想删除这些内容,则必须手动执行某些操作,例如指定难度模式并选择匹配每个难度级别的随机问题

答案 3 :(得分:1)

通过随机调整的输入数组进行迭代,每当你点击一个与前一个元素具有相同难度级别的元素时,与下一个没有相同难度级别的元素交换。就在我脑海中,我认为这会将您的初始输入转换为:1,5,1,2,3,2,3,2,4,2

根据输入的不同,这种方法最终可能会导致结块,但可能还不够......

如果你的输入大于你需要的输入,你也可以删除任何与之前相同难度的元素。

答案 4 :(得分:0)

一个非常简单的解决方案(虽然效率不高)是:

<?php

        define('MAX_QUESTIONS',10);

        $dbh = new PDO("mysql:dbname=so;host=127.0.0.1","","");
        $sql = "SELECT * FROM q group by difficulty order by rand()";
        $data = $dbh->query($sql);
        $rows = $data->fetchAll();
        $ids = getIds($rows);
        while (count($rows) < MAX_QUESTIONS ) {
                $sql = "SELECT * FROM q where id not in ".
                       "(".join(",",$ids).") group by difficulty order by rand()";
                $data = $dbh->query($sql);
                $more_rows = $data->fetchAll();
                $rows = array_merge($rows,$more_rows);
                $ids = getIds($rows);
        }
        print_r($rows);

        function getIds($data) {
                $ids = array();
                foreach ($data as $v) {
                        $ids[] = $v['id'];
                }
                return $ids;
        }

?>

这是必需的,因为MySQL的组始终返回相同的ID,无论您之前是否订购过(即使在子查询中)。

关于这一点的好处是,它保证没有'团块'(以最后一个问题返回空的潜在成本会产生'丛集',但你可能会遇到特殊情况)

糟糕的是,您需要多个查询,并且rand()的排序非常低效,但如果您的表很小,那么它可能实际上并不重要。