Хорошо, подробней, так подробней.
Сначала на пальцах, перенося задачу из программирования в реальный мир. Так понятнее суть уловить. Задача в том, чтобы N пронумерованных шариков, находящихся в мешке, расположить в случайном порядке. Как мы будет действовать?
- Вытаскиваем из мешка произвольный шарик из N шариков, кладём его на первое место.
- Вытаскиваем из мешка произвольный шарик из N-1 шариков, кладём его на второе место.
...
- Вытаскиваем из мешка один оставшийся шарик, кладём его на N-ное место.
Теперь вернёмся обратно в мир программрования.
У нас есть функция rand(), которая возвращает при каждом вызове "большое" случайное число. Если мы хотим получить случайное число от 0 до N-1, то нужно вычислить остаток от деления этого большого числа на N. В формуле это будет выгляеть rand() % N.
Что дальше? С программной точки зрения удобнее не "вытаскивать шарики из мешка", а на каждом шагу в этом же мешке их просто отодвигать в сторонку. К примеру, если у нас пронумерованные шарики - это вектор целых чисел, то в этом векторе будет граница перемешанных шариков: изначально она будет прижата к левой границе вектора и не будет содержать ни одного числа слева от себя, а во время работы алгоритма будет двигаться к концу вектора.
Ещё одно небольшое замечание. Благодаря тому, что шарики мы не вытаскиваем из мешка, а просто отодвигаем в сторону, последний шаг нам делать не нужно, так как один оставшийся шарик уже лежит на нужном месте.
От слов к делу:
Код: Выделить всё
#include <iostream>
#include <vector>
#include <algorithm>
#include <time.h>
void mix_vector(std::vector<int>& vec)
{
if (vec.size() <= 1)
{
return; // вектор не нужно смешивать, если он пустой или содержит один элемент
}
srand(time(NULL)); // инициализация генератора случайных чисел
int nBoundary = 0; // граница перемешанных шариков
const int nRightBoundary = vec.size() - 1; // правая граница, до которой мы смешиваем.
// Минус 1 потому, что последний шарик нет смысла выбирать, он и так остался один.
while (nBoundary < nRightBoundary)
{
const int nNotMixedCount = vec.size() - nBoundary; // количество непемеремешанных шариков
const int nSelectedNumber = rand() % nNotMixedCount; // номер шарика, который нужно отложить в сторону
const int nSelectedIndex = nBoundary + nSelectedNumber; // индекс выбранного шарика в векторе
std::swap(vec[nBoundary], vec[nSelectedIndex]); // отодвигаем выбранный шарик в сторонку
++nBoundary; // сдвигаем границу
}
}
void print_vector(std::vector<int>& vec)
{
std::vector<int>::const_iterator it = vec.begin();
for (; it != vec.end(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
int main()
{
std::vector<int> vec;
vec.push_back(0);
vec.push_back(1);
vec.push_back(2);
print_vector(vec);
mix_vector(vec);
print_vector(vec);
return 0;
}
Вывод программы:
0 1 2
2 0 1
Причём при повторных запусках, вторая строка меняется в произвольном порядке.
Специально для критиков напишу, что функция имплементирована намерянно в максимальном количестве строк. Всё ради того, чтобы можно было сделать коментарий для каждого выполняемого действия. Понятно, что функцию можно переписать чуть ли не в две строки, убрав временные переменные, и соорудив из них общую формулу. Так же можно заменить while на for. Помимо этого не исключено преобразование функции в темплейтную функцию, принимающую вектор с элементами любого типа, так как семантику типа элемента мы нигде не использовали. При желании топик стартер может провести эту работу сам.