返回特定圆中k个元素的组合的算法

时间:2018-12-22 18:34:45

标签: algorithm pseudocode

它打算使用k种不同的颜色(具有可通过数字切换的颜色名称)来绘制具有n个圆形冠和m个扇区的光盘。为了使光盘的绘画具有某种多样性,但要使差异变得模糊,绘画必须遵守以下规则:

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均按照规则进行了绘制。像右边的一样,并不是“黑白白黑”图案重复出现,而是大部分内部冠的相邻扇区(上面的扇区与其相邻的邻居不同)附近的重复扇区。

enter image description here

我已经尝试了一些算法,例如使用简单的组合,但是由于它是一个圆形,所以它不起作用,因此最后一个颜色集必须与第一个匹配。

1 个答案:

答案 0 :(得分:0)

据我了解,这个问题要求一种算法来生成长度为 n 的循环 k -ary格雷码。 (我假设目的是 m 总是正好是 k n ,以便所有可能的颜色序列出现在圆圈中。有关如何处理较短的Gray序列的说明,请参见下文。)

格雷码是一种编码系统,其中连续码的汉明距离为1;也就是说,它们仅在一个位置上有所不同。虽然最常用的格雷码是二进制的,但是对于任何 k ,该概念和算法都可以轻松扩展为以 k 为基础。

经典二进制格雷码是通过对数字从“左到右”(即高位在前)进行异或运算而由二进制数形成的。从Wikipedia复制而未修改的以下算法将XOR替换为“和模k”;如果 k 为2,它也起作用,因为XOR恰好是其参数的模2和。 [注1和注2]

我添加了一个驱动程序,该程序打印出 n 个长度序列的 k n 不同序列 k 种颜色,重复第一行以便清楚表明它是循环的。

实际上,很容易证明最后一个序列与第一个序列只有一个位置不同,因为将算法应用于 k n −1是完全由数字 k −1组成的基数 k ,在除第一个位置之外的所有其他位置都为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 的不完整Gray序列的情况。我在这里假设 k > 2是因为对于 k = 2,并非所有 m 值都具有解。 (特别是,如果 k 为2且 m 为奇数,则没有解决方案;这可以通过一个简单的奇偶校验参数来证明。)

首先,假设 m ≥2 k n-1

如果在最终位置上它与前任和前任都不同,我将在灰色序列 final 中将其称为元素。可以看出, final 元素出现在长度为 k −2的游程中,由成对的非final元素分隔。可以删除最终元素,因为其前任和后继还必须仅在最终位置上彼此不同。有2 k n-1 个非最终元素,如果 m ≥2 k < / i> n−1 我们可以删除 k n - m 个最终元素,并且仍然具有Gray序列。此子序列可以通过以下过滤器生成:

  • 非最终元素
  • 第一个 m −2 k n-1 最终元素

第二种情况是 m 的值是偶数且小于 k k n-1 ,其中 k '是不大于 k 的最大偶数。 (也就是说,如果 k 是偶数,则 k '是 k ,如果 k,则 k −1 很奇怪。)在这种情况下,我们可以利用反射格雷码的子序列。具体来说,对于混合基数,我们使用Gray序列,其数字权重为 k ',代表高位数字位置, k 为所有其他数字。我们从序列开始:

  • G n ,由上述算法生成的序列
  • Ĝ n ,与上述算法生成的序列相反

现在,我们将序列 S 定义如下:

  • S =⟨0 G n −1 ⟩1Ĝ n −1 ⟩…⟨ k 'Ĝ n −1 ⟩(其中⟨x G ⟩表示 G ,其中x附加在每个元素之前)。

应该清楚,从 S 开始的第 i 个元素和从结束的第 i 个元素strong> S 的区别仅在于其第一位数字。因此,我们可以使用 S 来生成长度为2 i 的Gray序列,直到 k '的任何 i / 2。

最后,如果我们需要长度为 m 的序列,其中 m 是奇数且小于 k k n-1 ,我们可以使用上面的结构,而忽略序列中的第二个元素。


注释

  1. 该代码将高位数字放置在数组的最后位置,因此“数字”将首先有效地打印低位数字。尽管回想起来,我最好还是向后打印序列。

  2. 维基百科的编辑历史表明,所引用的代码存在争议。万一它再次消失,我在代码的注释中添加了带时间戳的(即永久性)URL。