Иногда возникает в программировании задача, когда надо реализовать переменное количество вложенных циклов, число которых неизвестно, неизвестно также количество повторений каждого цикла. Как быть? В статье предлагается способ с использованием одного лишь цикла, который имитирует множество вложенных циклов.

Идея

Количество итераций во множестве вложенных циклов равно произведению прогонов каждого вложенного цикла. Поэтому можно заменить все одним циклом с числом повторений равным этому произведению. Но в циклах важную роль играют индексы, которые меняются от итерации к итерации. Поэтому для имитации вложенности всех циклов надо для каждой итерации нашего одного цикла ставить в соответствие вектор индексов вложенных циклов. Вот этим и займемся.

У меня эта задача возникла при тестировании алгоритмов оптимизации, где множество параметров и их количество у алгоритмов может отличаться, а переписывать каждый раз код из-за разных внешних вложенных циклов не хотелось.

Подготовительные функции

Для реализации идеи вам потребуется две функции, которые приведены ниже:

template <typename T> T productOfElementsOfVector(T *vector, int n)
{
/*
Функция вычисляет произведение элементов вектора.
Входные параметры:
 vector - указатель на исходный массив;
 n - размер массива.
Возвращаемое значение:
 Произведение элементов массива.
*/
  T result = 1;
  for (int i = 0; i < n; i++)
    result *= vector[i];

  return result;
}

void mixedMultiLogicVectorOfFullSearch(int *vector, int number, int *howMuchInElements, int n)
{
/*
Функция генерирует определенный вектор k-значной логики, где каждый элемент может
принимать разное максимальное значение, в полном переборе вариантов.
Генерируется number вектор в этом полном переборе.
Входные параметры:
 vector - выходной вектор, в который записывается результат;
 number - номер в массиве в полном переборе, начиная с нуля (от 0 и до произведения всех элементов массива howMuchInElements - 1);
 howMuchInElements - сколько значений может принимать элемент в векторе. То есть элемент может быть 0 и howMuchInElements[i] - 1;
 n - количество элементов в массиве.
Возвращаемое значение:
 Отсутствует.
*/
  int *countInBlock = new int[n];

  int countOfAllVariants = productOfElementsOfVector(howMuchInElements, n);

  countInBlock[0] = countOfAllVariants / howMuchInElements[0];

  for (int i = 1; i < n; i++)
    countInBlock[i] = countInBlock[i-1] / howMuchInElements[i];

  for (int i = 0; i < n; i++)
    vector[i] = (number / countInBlock[i]) % howMuchInElements[i];

  delete [] countInBlock;
}

Сама реализация

Всё использование показано в примере:

int N = 4;//сколько параметров будет у алгоритма
int *R = new int[N];//вектор числа повторений у циклов
R[0] = 3;//Число повторений у 1 цикла
R[1] = 3;//Число повторений у 2 цикла
R[2] = 3;//Число повторений у 3 цикла
R[3] = 2;//Число повторений у 4 цикла
int M = productOfElementsOfVector(R,N);//сколько итераций вообще будет
int *P = new int[N];//вектор текущих значений индексов имитируемых вложенных циклов

//основной цикл
for (int i = 0; i < M; i++) {
  mixedMultiLogicVectorOfFullSearch(P, i, R, N);//теперь в P лежат нужные индексы
  //используем результат
}
delete[] R;
delete[] P;

Если мы это запустим, то вектор P будет принимать значения:

P = 0  0  0  0

P = 0  0  0  1

P = 0  0  1  0

P = 0  0  1  1

P = 0  0  2  0

P = 0  0  2  1

P = 0  1  0  0

P = 0  1  0  1

P = 0  1  1  0

P = 0  1  1  1

P = 0  1  2  0

P = 0  1  2  1

P = 0  2  0  0

P = 0  2  0  1

P = 0  2  1  0

P = 0  2  1  1

P = 0  2  2  0

P = 0  2  2  1

P = 1  0  0  0

P = 1  0  0  1

P = 1  0  1  0

P = 1  0  1  1

P = 1  0  2  0

P = 1  0  2  1

P = 1  1  0  0

P = 1  1  0  1

P = 1  1  1  0

P = 1  1  1  1

P = 1  1  2  0

P = 1  1  2  1

P = 1  2  0  0

P = 1  2  0  1

P = 1  2  1  0

P = 1  2  1  1

P = 1  2  2  0

P = 1  2  2  1

P = 2  0  0  0

P = 2  0  0  1

P = 2  0  1  0

P = 2  0  1  1

P = 2  0  2  0

P = 2  0  2  1

P = 2  1  0  0

P = 2  1  0  1

P = 2  1  1  0

P = 2  1  1  1

P = 2  1  2  0

P = 2  1  2  1

P = 2  2  0  0

P = 2  2  0  1

P = 2  2  1  0

P = 2  2  1  1

P = 2  2  2  0

P = 2  2  2  1

То есть значения будут именно такими, какими были бы индексы во вложенных циклах. И все идет в том же порядке.