这个问题是关于在一个范围之间生成随机数,这很好,但不适合我的情况。
我将用 SQL 术语进行解释,因为在我看来这更容易理解,尽管问题是关于bash
.我的想法是用代码的结果bash
构建一个SQL脚本。
我有两张 MySQL 表,一张是people,一张是places。每条记录都有一个唯一的整数 ID,范围为 1 到 139(地点)和 1 到 1519(人)。它们通过外键相互联系,意思是:一个地方可以有很多人,但一个人只能有一个地方。
# 1-139 # 1-1519
place1 → person1
→ person2
→ person3
... and so on
我现在拥有的数据是在一个地方全部人民是有联系的,而其他地方则没有联系。
名额有139个,人数有1519人,所以我有1个名额,有1519人。
我的目标是将人员随机分配到各个地方,并且每个地方至少有一个人。
到目前为止我的代码是这样的:
$ c=1519
$ while [[ $c -ne 0 ]]; do
x=$((shuf -i 1-139 -n 1))
[[ $x -gt 139 ]] && continue
echo $x
(( c-- ))
done
此代码生成 1-139 之间的 1519 个随机数,因此现在我可以将每个人链接到一个随机位置。
我的问题是:
- 有没有更有效的方法来实现这一目标?
- 如何控制每个地方至少有一个人?
我更喜欢在 中执行此操作bash
,但我对不涉及它的其他解决方案持开放态度。
答案1
如果您只想使用常用工具(至少在 Linux 发行版上)来执行此操作,最有效的方法可能是询问shuf
:
shuf -i 1-139 -n 1519 -r
这会产生 1519 个在 1 到 139 之间随机选择的数字。
为保证每个地方都有一个人,先将139个数字洗乱,不要重复:
shuf -i 1-139
shuf -i 1-139 -n 1380 -r
为了减少“前 139 人”效应(前 139 人最终都会在不同的地方),请再次洗牌:
(shuf -i 1-139; shuf -i 1-139 -n 1380 -r) | shuf
答案2
假设表中存储了人员person
,并且每个人员都有place_id
一个 1 到 139 之间的整数。使用 SQL,person
直接更新表:
UPDATE person SET place_id = FLOOR(RAND()*139 + 1);
这应该更新表中的每个条目,随机化place_id
密钥。但它完全未经测试。
更新后,您可以使用以下方法测试每个地点是否代表
SELECT COUNT(DISTINCT place_id) = 139 FROM person;
如果所有地点都已表示,则应返回1
,否则返回0
。
答案3
如果我们希望这些值为尽可能均匀分布(考虑到 1519 比 139 的精确倍数少 10),而不是仅仅避免空位,那么我们应该生成一个足够大的重复序列 1, ..., 139, 1, ... , 139, 1, ...,然后对其中的前 1519 个成员进行洗牌:
while seq 139; do :; done | head -n 1519 | shuf
如果我们需要将人物映射到地点,那么我们可以简单地对输出行进行编号:
while seq 139; do :; done | head -n 1519 | shuf | nl
注意,这是所有标准外壳;不需要任何 Bash 扩展。