如何进行受控的“洗牌”命令?

发布于 2024-09-11 09:15:53 字数 837 浏览 7 评论 0原文

我在 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)的帮助都会很棒。

I have a set of quiz game questions in a sql database (javascript and sqlite actually). The questions all have a difficulty level from 1 to 5, 5 being hardest. Here is a simplified visualization of the data...


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

Now I can shuffle these fine in sql or code so they are in a random order with no repeats but I also want to have control over the way the difficulty field is ordered.

So for instance I could have a shuffled set of question where the difficulty level order looks like this...

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

This has several 'clumps' of difficulty, that's not what I want. The user playing the game would get several groups of the similarly difficult questions. An order like this would be better...

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

I want to ensure the questions are shuffled but without difficulty clumping. An even spread of difficulty where there are few, if any 'clumps'. Any help on the MySQL/javascript (or PHP) would be great.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

草莓酥 2024-09-18 09:15:53

与其将所有 id 分组在一起,为什么不按难度将它们分组,随机化每个部分,然后将它们一一拉出来。或者,一旦它们被随机排序,您就可以将它们从随机难度中拉出,然后删除该难度级别,直到每个问题都有一个问题。

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

只要所有其他选择加起来等于最大组减一,就不会出现聚集(假设您的算法是正确的)。然而,该算法基本上采用从 A(选择数量最多的组)中挑选、从另一组中挑选、从 A 中挑选等形式,直到 A 等于其他组的大小。因此,最好的算法会检查以找到最大的组并从中进行选择。然后它会从另一组中进行选择,然后检查哪个组是最大的,然后从中进行选择,除非它是之前选择的组等。

Instead of grouping all the ids together why don't you group them by difficulty randomize each section and then pull them out one by one. Or once they are randomly sorted you could pull them from a random difficulty, then remove that difficulty level until you have a question from each.

This is what I was thinking about in answer to sje397, so I'll add it to my answer.

As long as all the other choices add up to the largest group minus one you will have no clumping (assuming your algorithm is correct). However, the algorithm would basically take the form of pick from A (group with greatest number of choices), pick from another group, pick from A etc. until A is equal to the size of the other groups. So the best algorithm would check to find the largest group and pick from it. It would then pick from another group, then check to see what group is the largest, then choose from it unless it is the previously chosen one etc.

挽容 2024-09-18 09:15:53

代码中的以下策略怎么样:(以下是项目符号列表,但我无法使项目符号列表后面出现的代码正确显示 - 我彻底讨厌该网站使用的这种“markdown”垃圾)

按难度对问题进行排序

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

从简单和困难列表中逐一取出问题,在两者之间交替。 (这意味着您在问题序列中会有从简单到困难的轻微趋势,您可能会或可能不会接受。)

原始实现:

$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 是一个包含问题的数组按照我的建议订购。

How about the following strategy, in code: (the following was a bulleted list, but I couldn't get code appearing after a bulleted list to display correctly - I thoroughly detest this "markdown" garbage this site uses)

order the questions by difficulty

split the questions halfway into two lists, an "easy" list and a "hard" list

take questions one by one from the easy and hard lists, alternating between the two. (This would mean that you would have a slight trend from easy to difficult over the sequence of questions, which you might or might not be OK with.)

Primitive implementation:

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

Now $questions is an array containing questions ordered as I suggested.

噩梦成真你也成魔 2024-09-18 09:15:53

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

Well in a truly random sample 'clumps' do naturally appear. So if you want to remove these you have to enforce something manually, e.g. specify a pattern of difficulty and choosing a random question matching each difficulty level

小红帽 2024-09-18 09:15:53

迭代随机打乱的输入数组,每当您遇到与之前的元素具有相同难度级别的元素时,请与下一个不具有相同难度级别的元素交换。在我的脑海中,我认为这会将您的初始输入变成: 1,5,1,2,3,2,3,2,4,2

根据输入,这种方法可能会导致最后结块,但可能足够好...

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

Iterate through a randomly-shuffled input array and whenever you hit an element with the same difficulty level as the one before it, swap with the next element that doesn't have the same difficulty level. Just in my head, I think that this would turn your initial input into: 1,5,1,2,3,2,3,2,4,2

Depending on the input, this approach might cause clumping at the end but might be good enough...

If your input is bigger than what you need, you could also just remove any element that has the same difficulty as the one before it.

眉黛浅 2024-09-18 09:15:53

一个非常简单的解决方案(虽然不是很有效)是这样做的:

<?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 的 group by 总是返回相同的 id,无论您之前是否订购过(即使在子查询中)。

这样做的好处是它保证没有“团块”(以最终问题返回空的潜在成本会创建“团块”,但您可以特殊情况)

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

A very simple solution (it's not very efficient though) would be to do:

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

?>

This is needed because MySQL's group by always return the same ids, regardless of if you've ordered previously (even in a subquery.)

The good thing about this is that it guarantees no 'clumps' (at the potential cost of returning empty for the final question that would create a 'clump', you could special case that, though)

The bad thing is that you need more than a single query, and that ordering by rand() is awfully inefficient, but if your table is small it probably won't actually matter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文