Алгоритм возврата комбинаций k элементов в определенном круге

Он предназначен для раскраски диска с n круглыми коронками и m секторами, используя k различных цветов (названия цветов можно переключать по номерам). Для того, чтобы картина дисков была немного разнообразна, но различия должны быть размытыми, картина должна соответствовать следующим правилам:

1- каждый сектор в каждой короне имеет только один цвет

2- не может быть двух секторов с одинаковыми настройками цвета

2-два соседних сектора могут отличаться по цвету только от одной из своих коронок

С диска с n=2, m=9 e K=3 мы можем получить этот список

[

  [ "red"; "red" ],
  [ "red"; "green" ],
  [ "red"; "blue" ],
  [ "green"; "blue" ],
  [ "blue"; "blue" ],
  [ "blue"; "green" ],
  [ "green"; "green" ],
  [ "green"; "red" ],
  [ "blue"; "red" ] ]

Как вы можете видеть, последний сектор объединяется с первым в предложенных условиях...

Из дисков ниже, оба с n = 3, m = 8 и k = 2, только один слева окрашен в соответствии с правилами. Как и тот, что справа, это не "черно-бело-черный" шаблон, который повторяется, так как соседние секторы отличаются большей частью короны (сектор выше отличается от соседних соседей) более внутренним.

введите описание изображения здесь

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

1 ответ

Решение

Насколько я понимаю, этот вопрос требует алгоритма для генерации циклического k-го кода Грея длины n. (Я предполагаю, что намерение состоит в том, чтобы m всегда было точно k n, чтобы в круге присутствовали все возможные цветовые последовательности. См. Ниже примечание о том, как бороться с нахождением более коротких последовательностей Грея.)

Код Грея - это система кодирования, в которой последовательные коды имеют расстояние Хэмминга 1; то есть они отличаются только одной позицией. Хотя самый обычный код Грея является двоичным, концепция и алгоритм могут быть легко расширены до базы k для любого k.

Классический двоичный код Грея формируется из двоичного числа путем кумулятивного XOR-преобразования цифр "слева направо" (т. Е. Сначала старшего разряда). Следующий алгоритм, скопированный без изменений из Википедии, заменяет XOR на "сумма по модулю k "; это также работает, если k равно 2, потому что XOR является точно суммой своих аргументов по модулю 2. [Примечания 1 и 2]

Я добавил программу-драйвер, которая распечатывает k n различных последовательностей для любой n- длины последовательности k цветов, повторяя первую строку, чтобы прояснить, что она циклическая.

На самом деле, легко доказать, что последняя последовательность отличается только в одной позиции от первой последовательности, так как результат применения алгоритма к k n − 1, который является числом base- k, полностью состоящим из цифры k -1, имеет 0 в каждой позиции, кроме первой позиции.

// The following was copied without modification from
// https://en.wikipedia.org/w/index.php?title=Gray_code&oldid=869851753#n-ary_Gray_code

// inputs: base, digits, value
// output: Gray
// Convert a value to a Gray code with the given base and digits.
// Iterating through a sequence of values would result in a sequence
// of Gray codes in which only one digit changes at a time.
void toGray(unsigned base, unsigned digits, unsigned value, unsigned gray[digits])
{ 
        unsigned baseN[digits]; // Stores the ordinary base-N number, one digit per entry
        unsigned i;             // The loop variable

        // Put the normal baseN number into the baseN array. For base 10, 109 
        // would be stored as [9,0,1]
        for (i = 0; i < digits; i++) {
                baseN[i] = value % base;
                value    = value / base;
        }

        // Convert the normal baseN number into the Gray code equivalent. Note that
        // the loop starts at the most significant digit and goes down.
        unsigned shift = 0;
        while (i--) {
                // The Gray digit gets shifted down by the sum of the higher
                // digits.
                gray[i] = (baseN[i] + shift) % base;
                shift = shift + base - gray[i]; // Subtract from base so shift is positive
        }
}

/* Here is a simple driver program to demonstrate the sequence */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]) {
  if (argc < 3) {
    fprintf(stderr, "Usage: %s N colour...\n", argv[0]);
    argv = (char*[]){"3", "red", "green", "blue"};
  }
  unsigned n = atoi(argv[1]);
  unsigned k = argc - 2;
  argv += 2;
  int maxlen = 1;
  for (unsigned i = 0; i < k; ++i) if (strlen(argv[i]) > maxlen) maxlen = strlen(argv[i]);
  maxlen += 1; 

  unsigned gray[n];
  unsigned count = 1; for (unsigned i = n; i; --i) count *= k;

  for (unsigned v = 0; v <= count; ++v) {
    toGray(k, n, v, gray);
    for (unsigned i = 0; i < n; ++i) printf("%-*s", maxlen, argv[gray[i]]);
    putchar('\n');
  }
  return 0;
}

Вот несколько примеров выполнения этого кода:

./graynk 3 cyan yellow magenta    ./graynk 2 red green blue    ./graynk 3 black white
cyan    cyan    cyan              red   red                    black black black 
yellow  cyan    cyan              green red                    white black black 
magenta cyan    cyan              blue  red                    white white black 
magenta yellow  cyan              blue  green                  black white black 
cyan    yellow  cyan              red   green                  black white white 
yellow  yellow  cyan              green green                  white white white 
yellow  magenta cyan              green blue                   white black white 
magenta magenta cyan              blue  blue                   black black white 
cyan    magenta cyan              red   blue                   black black black 
cyan    magenta yellow            red   red   
yellow  magenta yellow  
magenta magenta yellow  
magenta cyan    yellow  
cyan    cyan    yellow  
yellow  cyan    yellow  
yellow  yellow  yellow  
magenta yellow  yellow  
cyan    yellow  yellow  
cyan    yellow  magenta 
yellow  yellow  magenta 
magenta yellow  magenta 
magenta magenta magenta 
cyan    magenta magenta 
yellow  magenta magenta 
yellow  cyan    magenta 
magenta cyan    magenta 
cyan    cyan    magenta 
cyan    cyan    cyan    

Теперь коротко рассмотрим случай, когда желательно получить неполную последовательность Грея длиной m < k n. Здесь я предполагаю, что k > 2, потому что для k = 2 не все значения m имеют решения. (В частности, если k равно 2, а m нечетно, решения не существует; это можно доказать с помощью простого аргумента четности.)

Сначала предположим, что m ≥ 2 k n − 1.

Я назову элемент в последовательности Грея финальным, если он отличается как от своего предшественника, так и от своего предшественника только в конечном положении. Видно, что конечные элементы появляются в сериях длиной k − 2, разделенных парами неконечных элементов. Последний элемент может быть удален, потому что его предшественник и его преемник также должны отличаться друг от друга только своей конечной позицией. Есть 2 k n − 1 не конечных элементов, и если m ≥ 2 k n − 1, мы можем удалить k n - m конечных элементов и при этом иметь последовательность Грея. Эта подпоследовательность может быть сгенерирована с помощью фильтра, который передает:

  • не финальные элементы
  • первые m -2 k n − 1 конечных элементов

Второй случай - это значения m, которые являются четными и меньшими, чем kk n-1, где k ' является наибольшим четным числом, не превышающим k. (То есть k 'равно k, если k четно, и k -1, если k нечетно.) В этом случае мы можем использовать подпоследовательность отраженного кода Грея. В частности, мы используем последовательность Грея для числа со смешанным основанием, веса цифр которого k 'для позиции старшего разряда и k для всех других цифр. Начнем с последовательностей:

  • G n, последовательность, сгенерированная вышеуказанным алгоритмом
  • & Gcirc; n, обратная последовательность, сгенерированная вышеуказанным алгоритмом

Теперь мы определим последовательность S следующим образом:

  • S = G0 G n −1⟩ & 1 & Gcirc; n − 1⟩… ⟨k ' & Gcirc; n − 1⟩ (где ⟨x G⟩ означает G с x приурочен к каждому элементу).

Должно быть понятно, что i-й элемент от начала S и i-й элемент от конца S отличаются только своей первой цифрой. Следовательно, мы можем использовать S для получения последовательности Грея длины 2 i для любого i вплоть до k '/ 2.

Наконец, если нам нужна последовательность длиной m, где m нечетно и меньше kk n − 1, мы можем использовать вышеупомянутую конструкцию, оставляя второй элемент в последовательности.


Заметки

  1. Код помещает старшую цифру в последнюю позицию в массиве, так что "число" сначала печатается младшей цифрой. Это ничего не меняет, хотя в ретроспективе мне было бы лучше печатать последовательности задом наперед.

  2. История редактирования Википедии показывает, что цитируемый код является предметом спора. На случай, если он снова исчезнет, ​​я поместил в комментарии в коде URL с меткой времени (то есть постоянный).

Другие вопросы по тегам