Как перемешать массив, не давая элементам сместиться больше чем на заданное значение (N) с их оригинальной позиции? Скажем, для N=1: [1, 2, 3, 4, 5] может быть перемешан в [1, 3, 2, 4, 5] или [2, 1, 3, 5, 4], но не в [5, 2, 1, 3, 4]
Нет ли случайно какого-нибудь алгоритма? Увы, нагуглить ничего не удаётся, а моя реализация не очень-то равномерная.
Тем не менее, приведу на всякий случай:
///
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 проверял).
Выкладываю код полностью, с простейшей проверкой на равномерность генерации.
Интересны мнения по поводу корректности данной наркомании :)
map
void operator delete (void *A){};
void operator delete[] (void *A){};
int n, k;
vector < vector
bool try_kuhn (int v) {
if (used[v]) return false;
used[v] = true;
for (size_t i=0; i
for (int Z=0;Z<5000;Z++){
vector
for (int i=0;i
Комментариев нет:
Отправить комментарий