如何在不让元素从其原始位置移动超过给定数量 (N) 的情况下对数组进行洗牌?假设 N=1:[1, 2, 3, 4, 5]可以混入[1, 3, 2, 4, 5]or [2, 1, 3, 5, 4],但不能混入[5, 2, 1, 3, 4].
有没有任何算法?唉,我什么都google不了,我的实现也不是很统一。
但是,以防万一:
/// <summary>
/// Возвращает перемешанный заданным образом массив размером size с элементами от 0
/// до size−1. В дальнейшем этот массив будет использован как список новых позиций.
/// </summary>
/// <param name="size">Число элементов</param>
/// <param name="limit">Максимальное смещение</param>
/// <returns>Перемешанный список индексов</returns>
private int[] Shuffle(int size, int limit) {
var buffer = new int[size];
for (var i = 0; i < buffer.Length; i++) {
buffer[i] = i;
}
for (var i = buffer.Length - 1; i >= 0; i--) {
// Узнаём оригинальную позицию элемента
var t = buffer[i];
// Границы для поиска относительно оригинальной позиции
var a = Math.Max(t - limit, 0);
var b = Math.Min(t + limit, buffer.Length - 1);
// Выбираем кандидата на обмен
var n = _randomInstance.Next(a, b + 1);
// Самого на себя не меняем
if (n != i) {
// Возможные границы обмена для найденного элемента
var ai = Math.Max(i - limit, 0);
var bi = Math.Min(i + limit, buffer.Length - 1);
// Узнаём оригинальную позицию элемента
var v = buffer[n];
// Если оригинальная позиция найденного вписывается в границы
// вокруг i, можно заменить
if (v >= ai && v <= bi) {
buffer[i] = buffer[n];
buffer[n] = t;
}
}
}
return buffer;
}
我不知道我的想法是否正确。因为 复杂性对我们来说不是很感兴趣,然后我们重新表述问题,将其减少到最大匹配。我们将有 N * 2 * Delta 阶的边。像往常一样复杂 - O(N^3)。
结合通过通常的 random_shuffle 进行的随机洗牌,这给出了所有可能的组合,并且或多或少是均匀的(我检查了小 N / Delta)。
我发布了完整的代码,对生成的一致性进行了最简单的检查。
关于这种毒瘾的正确性的有趣观点:)
我刚刚从这里复制了匹配代码
尽管如此,我尝试在给定范围内的其他答案中不止一次地实现random_shuffle(即,在 O (n) 时间内洗牌)。
这不是一个完整的解决方案,而是一个测试想法的工具。与您的版本相比的主要变化是,如果我们不喜欢随机排列,即在我们看来,它们的位置上保留了太多数字,那么我们会尝试重新排列这对数字(或多次,由参数)。
它使用两个数组 --
a[]-- 洗牌后的数字和p[]-- 洗牌前数字索引的辅助数组,它们会不断变化。数组p用来判断这个数是否可以移动到RNG指定的数组索引处?这是程序(Linux 上的 gcc/g++)。
我运行了不同次数的交换尝试(第三个参数),坦率地说,我不明白什么才是更好的。尝试一下,也许您会比其他选项更喜欢其中一个选项。