Как я могу найти все перестановки, состоящие из 1 элемента из числа переменных массивов переменной длины?

У меня есть массив U массивов D, которые различаются по длине. Мне нужно иметь возможность возвращать все перестановки индексов массива, которые будут выбирать другую перестановку, состоящую из 1 элемента из каждого набора. Я также требую, чтобы этот alorithm был представлен как объект, который только запоминает последнюю перестановку и возвращает следующую перестановку с помощью метода get_next.

Например, U = [array_of_size_n1, array_of_size_n2, array_of_size_n3] Там будут n1*n2*n3 перестановки, каждый из которых будет 3.

Изменить: количество наборов также меняется.

Ответ 1

Если вы используете python, это часть стандартной библиотеки: itertools.product. Но предполагая, что вы этого не сделали, здесь псевдокодная версия.

// Create an initialised array of indexes.
int[] index0(arrays) {
    // We require all arrays to be non-empty.
    for a in arrays {
        assert len(a) != 0;
    }
    return new int[len(arrays)];
}

// Increment the indices. Returns false when the indices wrap round to the start.
bool next_index(indices, arrays) {
    for (i = len(indices) - 1; i >= 0; --i) {
        indices[i] += 1
        if indices[i] < len(arrays[i]) {
            return true;
        }
        indices[i] = 0;
    }
    return false;
}

Вы можете использовать его так (если ни один из ваших массивов не пуст). В этом примере выводится каждая комбинация элементов из массивов.

indices = index0(arrays); 
{
    for (i = 0; i < len(arrays); ++i) {
        print arrays[i][indices[i]];
    }
    print
} while next_index(indices);

Ответ 2

Вы можете просто сохранить счетчик для своей индивидуальной позиции в каждом массиве. В методе get_next увеличьте счетчик для одного и измените его по длине массива. Затем вы просто увеличиваете следующий счетчик каждый раз, когда предыдущий свернет до 0;

if (pos3 == array_of_size_n3 -1)
{
   if (pos2 == size_of_array_2 -1)
   {
       pos1 = (pos1 + 1) % size_of_array_1

   }
   pos2 = (pos2 + 1) % size_of_array_2
}
pos3 = (pos3 + 1) % size_of_array_3

print array1[pos1], array2[pos2], array3[pos3]

EDIT: в случае, когда количество массивов меняется, удерживайте переменные позиции в массиве. На самом деле, вероятно, было бы лучше в любом случае. Таким образом, вы можете ссылаться на переменную pos так же, как вы ссылаетесь на сам массив.

Ответ 3

Итак... что об этом не просто?

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

psuedocode с использованием синтаксиса С# s yield return:

foreach n1 in a1
    foreach n2 in a2
        foreach n3 in a3
            yield return (n1, n2, n3)

EDIT: если количество наборов меняется, вы можете использовать некоторую форму рекурсии:

function next(list)
    firstArray = list.first
    iterator = iterator(list.rest)
    if !iterator
        foreach i in firstArray
            yield return i
    else
        foreach i in firstArray
            while (iterator.hasNext)
                yield return (i, iterator.next)

Рассмотрим поведение, когда передан список длины 1, а затем рассмотрим поведение для списка длины 2 и убедитесь, что оно действительно работает.

Ответ 4

Чтобы понять, что сказал Анон, вы не просто зацикливаете на них. Вы сохраняете состояние в своем классе, чтобы вы знали, каков ваш последний индекс для каждого массива. Логика такая же, но вы не работаете в непрерывном цикле. Логика псевдокода:

get_next()
{
  oldn3 = this.n3;
  oldn2 = this.n2;
  oldn1 = this.n1;

  if(this.n3 == this.a3.Count)
     this.n3 = 0;
  else
     this.n3++;

  if(oldn3 > this.n3)
    if(this.n2 == this.a2.Count)
      this.n2 = 0;
    else
      this.n2++;

  if(oldn2 > this.n2)
    if(this.n1 == this.a1.Count)
      this.n1 = 0;
    else
      this.n1++;

  if(oldn1 > this.n1)
    return NO_MORE_PERMS;

  return [n1,n2,n3];  
}

getCurrent()
{
  return [n1,n2,n3];
}