由于最近老是隔三岔五地要写一些随机打乱字符的任务,每次首选都是fisher-yates算法,但是傻子风脑子不太灵光,老是忘记流程,遂记此博客作为记忆存档
算法简述
Fisher-Yates 洗牌算法 是一种用于生成有限序列的无偏随机排列的算法。简单说,它能真正做到等概率地随机打乱一个数组。
核心思想(现代版本,由 Knuth 提出):
从数组末尾开始,向前遍历。对于当前最后一个元素(索引 i),随机地从前面未固定的部分(包括它自己)选择一个元素(索引 j),并交换它们的位置。通过这种方式,每个元素在迭代过程中都有一次机会被放置到当前序列的“末尾”,这个“末尾”在后续的遍历中将被固定下来,不再改动。
算法步骤:
- 设当前索引
i为数组的最后一个元素的索引(arr.length - 1)。 - 当
i > 0时,循环:
a. 生成随机索引j:范围在[0, i](包括 0 和 i)之间的一个随机整数。
b. 交换元素:交换数组中索引为i和索引为j的两个元素。
c. 缩小范围:将i减 1,缩小随机选择的范围。 - 当循环结束(
i == 0),数组已被完全随机打乱。
为什么它是无偏的?
因为每个元素在每次迭代中,被放置到当前“末尾”位置的概率都是完全相等的(1 / (i+1))。这保证了总共 n! 种可能的排列结果,每一种出现的概率都相同。
(以上由deepseek回答)
于此,我推荐B站上一位up主的一期视频对此算法有较为详尽的介绍,不过演示的是js,无伤大雅吧
【面试题】经典洗牌算法JS实现;Fisher-Yates算法;数组随机算法_哔哩哔哩_bilibili
以下,由于鄙人最近在用java写任务,就顺带附加一个java实现此算法的例子吧
import java.util.Random;//导包
public class Test {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7,8,9,10};
Random r = new Random();
int temp;//定义临时变量
for (int i = arr.length - 1; i > 0; i--) {//写一个循环用于打乱数组
int index = r.nextInt(i + 1);
temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}
//输出数组查看打乱效果
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
}
ヾ(≧∇≦*)ゝ