找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
游戏黄埔已经开课啦,大家速速报名赶快上车
查看: 2023|回复: 2

《Shuffle》算法,中文洗牌算法,数组的随机排列源代码

[复制链接]

162

主题

33

回帖

891

积分

管理员

积分
891
发表于 2025-3-7 18:23:10 | 显示全部楼层 |阅读模式
算法特点:

原地操作:直接修改原数组,不产生额外内存开销

时间复杂度O(n):只需单次遍历即可完成随机排列

均匀分布:保证每个排列出现的概率相等

泛型支持:适用于任何类型的数组(int、string、自定义类等)

注意事项:

需要确保已经 using UnityEngine; 命名空间

如果需要在非主线程使用,建议使用 System.Random 替代并传入随机种子

对于List集合,可以直接用相同算法原理实现


这个实现是游戏开发中常用的标准洗牌方法,适用于卡牌游戏、随机道具生成、NPC行为随机化等场景。

  1. using UnityEngine;

  2. public static class ArrayExtensions
  3. {
  4.     /// <summary>
  5.     /// 使用Fisher-Yates算法随机打乱数组
  6.     /// </summary>
  7.     /// <param name="array">需要打乱的数组(会被直接修改)</param>
  8.     public static void Shuffle<T>(this T[] array)
  9.     {
  10.         // 从最后向前遍历数组
  11.         for (int i = array.Length - 1; i > 0; i--)
  12.         {
  13.             // 生成一个0到i(包含i)的随机索引
  14.             int randomIndex = Random.Range(0, i + 1);
  15.             
  16.             // 交换当前元素和随机位置的元素
  17.             T temp = array[i];
  18.             array[i] = array[randomIndex];
  19.             array[randomIndex] = temp;
  20.         }
  21.     }
  22. }
复制代码
  1. // 创建一个测试数组
  2. int[] myArray = { 1, 2, 3, 4, 5 };

  3. // 打乱数组
  4. myArray.Shuffle();

  5. // 输出结果
  6. foreach (int num in myArray)
  7. {
  8.     Debug.Log(num);
  9. }
复制代码
扩展用法:
如果需要保留原始数组并创建新的乱序副本,可以添加以下方法:
  1. public static T[] ShuffledCopy<T>(this T[] array)
  2. {
  3.     T[] copy = (T[])array.Clone();
  4.     copy.Shuffle();
  5.     return copy;
  6. }
复制代码



回复

使用道具 举报

162

主题

33

回帖

891

积分

管理员

积分
891
 楼主| 发表于 2025-3-7 18:42:56 | 显示全部楼层
1️⃣ 需求分析 若想实现类似 0-99 范围内 所有整数随机出现且不重复 的效果(类似抽奖号码的随机洗牌),可以按以下步骤实现:
  1. using UnityEngine;

  2. public class RangeShuffleExample : MonoBehaviour
  3. {
  4.     void Start()
  5.     {
  6.         // 生成 0-99 的数组并洗牌
  7.         int[] randomNumbers = GenerateShuffledRange(0, 99);
  8.         
  9.         // 按洗牌后的顺序使用数值
  10.         foreach(int num in randomNumbers)
  11.         {
  12.             Debug.Log(num); // 输出:随机排列的0-99
  13.         }
  14.     }

  15.     /// <summary>
  16.     /// 生成指定范围的随机乱序数组
  17.     /// </summary>
  18.     /// <param name="min">最小值(包含)</param>
  19.     /// <param name="max">最大值(包含)</param>
  20.     public static int[] GenerateShuffledRange(int min, int max)
  21.     {
  22.         // 参数校验
  23.         if (min > max) (min, max) = (max, min);

  24.         // 创建连续数字数组
  25.         int length = max - min + 1;
  26.         int[] numbers = new int[length];
  27.         for (int i = 0; i < length; i++)
  28.         {
  29.             numbers[i] = min + i;
  30.         }

  31.         // 使用Fisher-Yates算法洗牌
  32.         for (int i = length - 1; i > 0; i--)
  33.         {
  34.             int randomIndex = Random.Range(0, i + 1);
  35.             // 交换元素
  36.             int temp = numbers[i];
  37.             numbers[i] = numbers[randomIndex];
  38.             numbers[randomIndex] = temp;
  39.         }

  40.         return numbers;
  41.     }
  42. }
复制代码

2️⃣ 代码特点 特性 说明   无重复随机序列 生成 min 到 max 范围内的所有整数,且保证每个数字只出现一次  时间复杂度 O(n) 初始化数组 O(n) + 洗牌 O(n) = 总复杂度 O(n)  内存优化 直接操作数组,无需额外集合类  范围动态调整 支持任意整数范围(包括负数范围如 -5~5)
3️⃣ 两种典型应用场景 🔸 场景1:抽奖系统
  1. // 生成1-10000的奖励ID并洗牌
  2. int[] rewardIDs = GenerateShuffledRange(1, 10000);

  3. // 按顺序发放奖励(保证不重复)
  4. foreach(int id in rewardIDs)
  5. {
  6.     GiveReward(id);
  7. }
复制代码

🔸 场景2:随机关卡顺序
  1. // 生成0-9的关卡索引并洗牌
  2. int[] levelOrder = GenerateShuffledRange(0, 9);

  3. // 按洗牌后的顺序加载关卡
  4. LoadLevel(levelOrder[currentIndex]);
复制代码

4️⃣ 与 Random.Range 的对比
Random.Range(0,100)        Shuffle 生成的数组
重复性        允许重复        保证所有值唯一
内存占用        无额外内存        需要存储整个数组
适用场景        单次随机值需求(如随机伤害)        需要不重复序列(如卡牌洗牌)
性能        O(1)        O(n)
5️⃣ 进阶用法:延迟生成 如果需要 动态获取随机数且避免重复,可以使用迭代器模式:
  1. public class RandomSequenceGenerator
  2. {
  3.     private int[] _shuffledArray;
  4.     private int _currentIndex;

  5.     public RandomSequenceGenerator(int min, int max)
  6.     {
  7.         _shuffledArray = GenerateShuffledRange(min, max);
  8.         _currentIndex = 0;
  9.     }

  10.     public int Next()
  11.     {
  12.         if (_currentIndex >= _shuffledArray.Length)
  13.         {
  14.             Debug.LogWarning("已到达序列末尾");
  15.             return -1;
  16.         }
  17.         return _shuffledArray[_currentIndex++];
  18.     }
  19. }

  20. // 使用示例
  21. var generator = new RandomSequenceGenerator(0, 99);
  22. Debug.Log(generator.Next()); // 输出第一个随机数
  23. Debug.Log(generator.Next()); // 输出第二个随机数
复制代码

6️⃣ 数学原理 Fisher-Yates 洗牌算法通过以下步骤保证 均匀分布: 1.  从最后一个元素开始向前遍历  2.  每次随机选择一个 当前位置之前(包含当前位置) 的索引  3.  交换这两个位置的元素   通过这种操作,每个元素出现在每个位置的概率均等,最终生成的排列总数是 n!(n的阶乘),确保随机性质量。   如果需要 仅生成单个随机数 而不是序列,直接使用 Random.Range 即可,洗牌算法在这种场景下反而是性能浪费。务必根据实际需求选择最合适的随机方案。
回复

使用道具 举报

162

主题

33

回帖

891

积分

管理员

积分
891
 楼主| 发表于 2025-3-7 19:38:45 | 显示全部楼层
c# shuffle算法得出的数组,按照它们的下标顺序组合成一个整数。比如数组是[3,1,2],下标0是3,1是1,2是2,组合成整数312
实现步骤
将数组元素转换为字符串:使用 string.Join 直接拼接数组元素。

字符串转整数:使用 int.Parse 或 int.TryParse 确保转换安全。
  1. using System;

  2. public class Program
  3. {
  4.     public static void Main()
  5.     {
  6.         int[] shuffledArray = {3, 1, 2};
  7.         
  8.         // 将数组元素拼接为字符串
  9.         string combined = string.Join("", shuffledArray);
  10.         
  11.         // 转换为整数
  12.         int result = int.Parse(combined);
  13.         
  14.         Console.WriteLine(result); // 输出:312
  15.     }
  16. }
复制代码

关键点说明
string.Join("", array):
将数组元素直接拼接成一个连续字符串(例如 [3,1,2] → "312"),无需显式遍历数组。

int.Parse 的安全性问题:

如果数组可能包含非数字字符(如 ['a', 2])或空数组,建议改用 int.TryParse 避免异常。

超大整数(超过 int 范围)需改用 long.Parse 或 BigInteger。
扩展场景
1. 处理多位数元素
如果数组元素是多位数(例如 [12, 34, 5] → 组合为 12345),上述代码仍然有效。

2. 安全转换(TryParse)
  1. if (int.TryParse(combined, out int safeResult))
  2. {
  3.     Console.WriteLine(safeResult);
  4. }
  5. else
  6. {
  7.     Console.WriteLine("无效的整数格式");
  8. }
复制代码

3. 数学方法(仅限元素为 0-9)
若数组元素均为 0-9 的数字,可用逐位计算:
  1. int result = 0;
  2. foreach (int digit in shuffledArray)
  3. {
  4.     result = result * 10 + digit;
  5. }
  6. Console.WriteLine(result); // 输出:312
复制代码

总结
字符串拼接法:通用性强,支持任意整数元素。

数学逐位法:仅适用于元素为 0-9 的情况,性能略高。

根据实际需求选择合适的方法即可。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|平顶山市图灵科技 ( 豫ICP备2024088136号-1| 豫公网安备41040202000275号 )

GMT+8, 2025-5-22 21:57 , Processed in 0.049413 second(s), 20 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表