I wrote the two methods below to automatically select N distinct colors. It works by defining a piecewise linear function on the RGB cube. The benefit of this is you can also get a progressive scale if that's what you want, but when N gets large the colors can start to look similar. I can also imagine evenly subdividing the RGB cube into a lattice and then drawing points. Does anyone know any other methods? I'm ruling out defining a list and then just cycling through it. I should also say I don't generally care if they clash or don't look nice, they just have to be visually distinct.

public static List<Color> pick(int num) {
    List<Color> colors = new ArrayList<Color>();
    if (num < 2)
        return colors;
    float dx = 1.0f / (float) (num - 1);
    for (int i = 0; i < num; i++) {
        colors.add(get(i * dx));
    }
    return colors;
}

public static Color get(float x) {
    float r = 0.0f;
    float g = 0.0f;
    float b = 1.0f;
    if (x >= 0.0f && x < 0.2f) {
        x = x / 0.2f;
        r = 0.0f;
        g = x;
        b = 1.0f;
    } else if (x >= 0.2f && x < 0.4f) {
        x = (x - 0.2f) / 0.2f;
        r = 0.0f;
        g = 1.0f;
        b = 1.0f - x;
    } else if (x >= 0.4f && x < 0.6f) {
        x = (x - 0.4f) / 0.2f;
        r = x;
        g = 1.0f;
        b = 0.0f;
    } else if (x >= 0.6f && x < 0.8f) {
        x = (x - 0.6f) / 0.2f;
        r = 1.0f;
        g = 1.0f - x;
        b = 0.0f;
    } else if (x >= 0.8f && x <= 1.0f) {
        x = (x - 0.8f) / 0.2f;
        r = 1.0f;
        g = 0.0f;
        b = x;
    }
    return new Color(r, g, b);
}

当前回答

这在MATLAB中是微不足道的(有一个hsv命令):

cmap = hsv(number_of_colors)

其他回答

我为R写了一个名为qualpalr的包,它是专门为此目的设计的。我建议你看看小插图,看看它是如何工作的,但我会尽量总结要点。

qualpalr在HSL颜色空间(前面在这个线程中描述过)中获取一个颜色规范,将其投射到DIN99d颜色空间(感知上是均匀的),并找到使它们之间的最小距离最大化的n。

# Create a palette of 4 colors of hues from 0 to 360, saturations between
# 0.1 and 0.5, and lightness from 0.6 to 0.85
pal <- qualpal(n = 4, list(h = c(0, 360), s = c(0.1, 0.5), l = c(0.6, 0.85)))

# Look at the colors in hex format
pal$hex
#> [1] "#6F75CE" "#CC6B76" "#CAC16A" "#76D0D0"

# Create a palette using one of the predefined color subspaces
pal2 <- qualpal(n = 4, colorspace = "pretty")

# Distance matrix of the DIN99d color differences
pal2$de_DIN99d
#>        #69A3CC #6ECC6E #CA6BC4
#> 6ECC6E      22                
#> CA6BC4      21      30        
#> CD976B      24      21      21

plot(pal2)

这里有一个解决你的“独特”问题的解决方案,这完全是夸大的:

创建一个单位球体,并在其上放置带有排斥电荷的点。运行一个粒子系统,直到它们不再移动(或者delta“足够小”)。在这一点上,每个点之间的距离都尽可能远。将(x, y, z)转换为rgb。

我提到它是因为对于某些类型的问题,这种类型的解决方案比暴力解决方案更好。

我一开始看到这种方法是用来镶嵌球面的。

同样,遍历HSL空间或RGB空间的最明显的解决方案可能工作得很好。

我有个主意。想象一个HSV气缸

定义亮度和饱和度的上限和下限。这在空间内定义了一个正方形的横截面环。

现在,在这个空间中随机散布N个点。

然后对它们应用迭代排斥算法,要么迭代次数固定,要么直到这些点稳定下来。

现在你应该有N个点,代表N种颜色,它们在你感兴趣的颜色空间中尽可能不同。

Hugo

这在MATLAB中是微不足道的(有一个hsv命令):

cmap = hsv(number_of_colors)

Janus的回答,但更容易读懂。我还稍微调整了配色方案,并在你可以自己修改的地方做了标记

我已经把这个片段直接粘贴到一个jupyter笔记本。

import colorsys
import itertools
from fractions import Fraction
from IPython.display import HTML as html_print

def infinite_hues():
    yield Fraction(0)
    for k in itertools.count():
        i = 2**k # zenos_dichotomy
        for j in range(1,i,2):
            yield Fraction(j,i)

def hue_to_hsvs(h: Fraction):
    # tweak values to adjust scheme
    for s in [Fraction(6,10)]:
        for v in [Fraction(6,10), Fraction(9,10)]: 
            yield (h, s, v) 

def rgb_to_css(rgb) -> str:
    uint8tuple = map(lambda y: int(y*255), rgb)
    return "rgb({},{},{})".format(*uint8tuple)

def css_to_html(css):
    return f"<text style=background-color:{css}>&nbsp;&nbsp;&nbsp;&nbsp;</text>"

def show_colors(n=33):
    hues = infinite_hues()
    hsvs = itertools.chain.from_iterable(hue_to_hsvs(hue) for hue in hues)
    rgbs = (colorsys.hsv_to_rgb(*hsv) for hsv in hsvs)
    csss = (rgb_to_css(rgb) for rgb in rgbs)
    htmls = (css_to_html(css) for css in csss)

    myhtmls = itertools.islice(htmls, n)
    display(html_print("".join(myhtmls)))

show_colors()