如何高效从易记单词中选取3个唯一字符?Random对象创建疑问
优化随机选取单词中3个唯一字符的实现 + Random对象创建的疑问解答
现有代码的问题
你当前的代码通过循环生成随机索引,用List.Contains()判断是否重复,这种方式的问题在于List.Contains()是O(n)时间复杂度,每次判断都要遍历已选列表,当单词较长或需要选取的字符数量增加时,效率会下降;而且可能多次生成重复索引,导致循环次数增多。
更高效的实现方案
方案1:Fisher-Yates洗牌法(最优)
直接生成所有索引的数组,通过洗牌打乱顺序后取前3个,一次操作就能得到唯一的随机索引,无需循环重试,效率极高。
Random rand = new Random(); // 生成单词所有字符的索引数组 int[] indices = Enumerable.Range(0, memorableWord.Length).ToArray(); // 仅打乱前3个位置的元素(无需全量洗牌,进一步提升效率) for (int i = 0; i < 3; i++) { int swapIndex = rand.Next(i, indices.Length); // 交换当前位置和随机选中的位置 (indices[i], indices[swapIndex]) = (indices[swapIndex], indices[i]); } // 取前3个唯一索引 List<int> selections = indices.Take(3).ToList();
方案2:HashSet替代List(适合超长单词)
如果单词长度极大,不想生成全量索引数组,可以用HashSet存储已选索引——HashSet.Contains()是O(1)时间复杂度,比List的查找快得多,而且HashSet.Add()会自动忽略重复值,无需额外判断:
HashSet<int> selectionsSet = new HashSet<int>(); Random rand = new Random(); while (selectionsSet.Count < 3) { int index = rand.Next(memorableWord.Length); selectionsSet.Add(index); // 重复值会被自动忽略 } // 如需转为List List<int> selections = selectionsSet.ToList();
关于Random对象创建的疑问
绝对不要在循环里每次创建新的Random对象!
原因是:Random的默认构造函数依赖系统时钟作为种子,如果循环执行速度极快,连续创建的Random对象会使用相同的种子,导致生成的随机数完全重复,根本无法得到不同的索引,甚至会陷入死循环。
正确的做法是在循环外只创建一次Random对象,重复调用它的Next()方法,这样才能生成真正随机的序列,也就是你原代码里的写法才是正确的,循环内创建新对象的方式不仅不优,还会导致严重的随机数重复问题。
内容的提问来源于stack exchange,提问作者Glyn
相关产品推荐
相关产品推荐

