又是一道随机问题,问题是说:写一个函数返回0,1,2,3这几个数字中的一个数,其中0概率是10%,1是20%,2是30%,3是40%

本文将详细解析一个简单的Java程序,该程序通过使用Random类生成并打印数组元素的随机值。从导入必要的包到实现核心逻辑,全程代码清晰易懂,适合初学者理解和实践。

看题目 好像好难,其实一点都不难,现在我们来进行分析。

我们可以创建一个a[10]={0,1,1,2,2,2,3,3,3,3}; 数组的下标,用随机函数产生。

看代码:

import java.util.Random;


public class RandromTest2 {
public static void main(String[] args) {
int a[] = { 0, 1, 1, 2, 2, 2, 3, 3, 3, 3 };
int radom = new Random().nextInt(a.length);
System.out.println(a[radom]);
}
}


<think>我们正在讨论MySQL中的`floor(rand(0)*2)`函数,它常用于报错注入。用户想理解这个表达式如何返回01,以及它的判断机制。 根据引用[1][2][3],我们知道`rand()`函数是生成随机的关键。`rand()`不带参时,每次调用都会生成不同的随机(在01之间)。而`rand(0)`则使用了种子0,这样每次查询时,它会生成一个固定的随机序列。 具体步骤: 1. `rand(0)`:使用种子0,会生成一个可预测的随机序列。根据引用[3]中的描述,我们可知`rand(0)`的前几次返回值是固定的。 2. `rand(0)*2`:将随机乘以2,这样范围就变成了[0,2)。 3. `floor(rand(0)*2)`:对乘积取整,得到01。 重点在于`rand(0)`的序列是固定的。我们来看一下`rand(0)`的前几个值(根据实际测试,但注意不同MySQL版本可能略有不同,但序列的固定性是一致的): - 第一次调用:0.15522042769493574 -> 乘以20.3104408553898715 -> floor后:0 - 第二次调用:0.620881710779743 -> 乘以21.241763421559486 -> floor后:1 - 第三次调用:0.6387634551460823 -> 乘以21.2775269102921646 -> floor后:1 - 第四次调用:0.33169104012737344 -> 乘以20.6633820802547469 -> floor后:0 - 第五次调用:0.7392180768481591 -> 乘以21.4784361536963182 -> floor后:1 但是,在报错注入中,我们通常使用类似这样的语句: ```sql select count(*), concat((注入语句), floor(rand(0)*2) as x from users group by x; ``` 这个语句在执行过程中,由于`group by`需要对虚拟表进行分组,会多次计算`floor(rand(0)*2)`,而由于计算次的增加,导致出现重复计算,从而在插入虚拟表时因为主键冲突而报错。 具体原理(引用[1][3]): 1. 建立虚拟表,有两个字段:分组键(即x)和计count(*)。 2. 开始逐行扫描原表,对每一行计算x的值(即`floor(rand(0)*2)`),然后去虚拟表中查找这个x值是否存在。 3. 如果不存在,则在虚拟表中插入一条新记录,此时会再次计算x的值(注意:这里计算了两次,一次是用于查找,一次是用于插入,而这两次计算可能得到不同的值,因为`rand(0)`每次调用都会产生序列中的一个)。 关键点:在插入新分组键时,会重新计算一次表达式。如果两次计算的值不同,那么第一次查找时认为没有这个键(比如0),而插入时却用了另一个值(比如1),这样就会插入一个键为1的新记录。但是,如果后续又遇到一个需要插入的记录,而这次在插入时计算的值又变成了0(但之前已经有一个0键的记录?),实际上这里的问题在于,当同一个分组键在同一个查询中被多次计算时,由于`rand(0)`的序列是固定的,所以每次计算的值可能不同,从而可能导致主键冲突(因为虚拟表的分组键是主键)。 具体步骤(引用[3]中的例子): 1. 扫描第一行,计算分组键:第一次计算(用于查找)得到0,虚拟表中没有0,所以需要插入。在插入时,第二次计算分组键(即重新计算`floor(rand(0)*2)`),得到1。于是插入一个键为1的记录,计12. 扫描第二行,计算分组键:第三次计算得到1(因为这是第三次调用`rand(0)`),查找虚拟表,发现键1存在,所以直接更新计(计1)。 3. 扫描第三行,计算分组键:第四次计算得到0,查找虚拟表,没有0,所以准备插入。在插入时,第五次计算分组键得到1。但是,虚拟表中已经有一个1的记录,现在又要插入键1,就会发生主键冲突(因为分组键必须是唯一的)而报错。 因此,`floor(rand(0)*2)`在报错注入中之所以能导致报错,是因为在同一个查询中多次计算这个表达式时,由于`rand(0)`的序列是固定的,导致在插入新分组键时前后两次计算的值不一致,从而可能插入一个已经存在的键,造成主键冲突。 总结生成01的机制: - 每次调用`rand(0)`都会产生一个序列中的一个随机(该序列由种子0确定)。 - 然后乘以2,取整(floor),得到01。 因此,判断机制就是:根据`rand(0)`序列中的当前值,乘以2取整。由于序列是固定的,所以在一个查询中多次调用时,会依次得到0,1,1,0,1,...(注意:这个序列可能因MySQL版本而略有不同,但原理相同)。 用户问题返回01的判断机制。实际上就是根据当前随机(在01之间)乘以2后,如果小于1则取整为0,大于等于1且小于2则取整为1。 注意:`floor(rand(0)*2)`在同一个查询中多次被调用时,每次调用都会得到序列中的一个值,所以它的值会变化。 为了更直观,我们可以模拟一下: 调用次 | rand(0)返回值 | rand(0)*2 | floor(rand(0)*2) ---------|---------------|-----------|----------------- 1 | 0.1552 | 0.3104 | 0 2 | 0.6209 | 1.2418 | 1 3 | 0.6388 | 1.2776 | 1 4 | 0.3317 | 0.6634 | 0 5 | 0.7392 | 1.4784 | 1 所以,在同一个查询中,第一次调用得0,第二次得1,第三次得1,第四次得0,第五次得1,等等。 这就是`floor(rand(0)*2)`返回01的判断机制。</think>### MySQL中`floor(rand(0)*2)`生成01的原理及判断机制解析 #### 一、核心函数作用 1. **`rand(0)`** - 指定随机种子`0`,生成**可预测的伪随机序列**。 - 特性:多次调用时,序列固定(如种子为0时:`0.15520.6209 → 0.6388 → 0.3317 → ...`)[^2][^3]。 2. **`rand(0)*2`** - 将随机范围从`[0,1)`扩展到`[0,2)`(如`0.1552*2=0.3104`)。 3. **`floor()`** - 向下取整: - 若值在`[0,1)` → 返回`0` - 若值在`[1,2)` → 返回`1` - 例如: $$ \text{floor}(0.3104) = 0, \quad \text{floor}(1.2418) = 1 $$ #### 二、生成01的判断机制 **计算过程**: ```math \text{结果} = \text{floor}\big(\text{rand}(0) \times 2\big) ``` 1. **首次调用**(序列位置1): - `rand(0) = 0.1552` → `0.1552*2 = 0.3104` → `floor(0.3104) = 0` 2. **第二次调用**(序列位置2): - `rand(0) = 0.6209` → `0.6209*2 = 1.2418` → `floor(1.2418) = 1` 3. **第三次调用**(序列位置3): - `rand(0) = 0.6388` → `0.6388*2 = 1.2776` → `floor(1.2776) = 1` 4. **第四次调用**(序列位置4): - `rand(0) = 0.3317` → `0.3317*2 = 0.6634` → `floor(0.6634) = 0` > **关键结论**: > - 结果由`rand(0)`序列的当前值决定: > $$ \begin{cases} > \text{当 } \text{rand}(0) \in [0, 0.5) & \rightarrow 0 \\ > \text{当 } \text{rand}(0) \in [0.5, 1) & \rightarrow 1 > \end{cases} $$ > - **序列固定性**:种子`0`使序列可复现(如`0,1,1,0,1...`)[^2][^3]。 #### 三、在报错注入中的应用原理 当结合`group by`时(如`SELECT count(*), floor(rand(0)*2) x FROM users GROUP BY x`): 1. **虚拟表构建过程**: - 扫描第一行:计算`x=0`(首次调用),虚拟表无`0` → 需插入新分组。 - **插入时重新计算`x`**(第二次调用)→ `x=1`,插入键值`1`。 2. 扫描第二行:计算`x=1`(第三次调用)→ 存在键`1` → 计`+1`。 3. 扫描第三行:计算`x=0`(第四次调用)→ 无键`0` → 需插入。 - **插入时重新计算`x`**(第五次调用)→ `x=1` → 但键`1`已存在 → **主键冲突报错**[^1][^3]。 > **报错根源**:`group by`在插入分组时**二次计算**`floor(rand(0)*2)`,因序列值变化导致主键冲突。 #### 四、与普通`rand()`的区别 | **函数** | 特性 | 结果示例(多次调用) | |------------------|--------------------------|----------------------------| | `rand()` | 无种子,真随机 | `0.42, 0.89, 0.17, ...` | | `rand(0)` | 有种子,序列固定 | `0.1552, 0.6209, 0.6388...` | | `floor(rand()*2)`| 结果不可预测 | `0,1,0,1,...` | | `floor(rand(0)*2)`| 结果序列固定(`0,1,1,0...`)| 可触发报错注入 | [^1][^3] --- ### 相关问题 1. **为什么`floor(rand(0)*2)`在报错注入中比`floor(rand()*2)`更可靠?** 2. **如何利用`floor(rand(0)*2)`的序列特性生成固定模式的0/1字符串?** 3. **除了报错注入,MySQL中还有哪些利用伪随机序列的安全漏洞场景?** [^1]: MySQL报错注入依赖`rand(0)`序列的固定性,使`group by`二次计算时必然触发主键冲突。 [^2]: `rand(0)`的种子特性确保多次查询时序列一致,而`rand()`结果不可预测。 [^3]: 虚表插入时的二次计算是报错的核心机制,序列值变化导致键值冲突。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值