从字符串生成唯一的颜色

时间:2011-07-06 10:04:55

标签: .net algorithm colors

我必须在一些 opaque 矩形区域中绘制一些任意字符串。 我需要那个区域的背景颜色与给定的字符串是唯一的。

说,如果我画“巴黎”和“巴黎1”,他们的颜色应该是不同的。但如果我用蓝色绘制“巴黎”,那么彼此的“巴黎”也应该用蓝色绘制。

我再次强调颜色应该是不透明的。

为此,我找到了一个简单的解决方案:

rectangleBackColor = Color.FromArgb(myString.GetHashCode())

问题在于不透明度。我需要“消除”“A”(alpha)组件。

现在,这样的代码可以正常工作

  rectangleBackColor = Color.FromArgb(myString.GetHashCode())
  ' set the alpha value = 255 for an opaque color '
  rectangleBackColor = Color.FromArgb(255, rectangleBackColor) 

但它可以松散字符串颜色的唯一性。

说,我有两个不同的hashCodes(在hexa中)x AB 11 22 33x FF 11 22 33
AB设置为FF我将两个不同的字符串设置为相同的背景颜色(x FF 11 22 33)。这不好。

4 个答案:

答案 0 :(得分:2)

您无法为字符串生成唯一的颜色:

RGB空间具有24位或2 ^ 24-1个唯一值(总共16777215种颜色)。

以下代码将打印16777220个唯一字符串(比颜色数多5个):

for(int i=0; i<16777220 ; ++i) Console.WriteLine(i.ToString());

因此,如果您获取上面编程生成的所有字符串并给出颜色,则必须至少有5个重复颜色的字符串。

顺便说一下,GetHashCode不返回唯一值,只是均匀分布,用32位重复上面的证明(你需要在for循环uint中创建i并将数字更改为4294967295以上,但这些是只有更改)并且您发现无法为32位值(或任何其他固定大小值)中的每个字符串获取唯一值。

你的方法非常好,重复的颜色很少见。

答案 1 :(得分:2)

鉴于您无法生成真正唯一的整数来表示字符串,因为它们可以表示的空间大小不同,您可以尝试:

uint noA = (unit)myString.GetHashCode() / 255; 
uint opaque = noA + 0xFF000000; 
rectangleBackColor = Color.FromArgb(opaque);

这应该基于哈希码为每个字符串生成(相当)唯一值,并将alpha分量设置为255.显然,这只能生成2 ^ 24个不同的值,因此不是真正唯一的。 / p>

编辑:应该注意,这与您的版本完全相同,但忽略最低8位而不是最高位。

答案 2 :(得分:1)

哈希首先并不是独一无二的。如果您不相信,只需考虑比hashcode多多少个字符串。首先,哈希代码本身就是字符串!这意味着如果您要将不同的哈希代码归属到每个字符串,那么在遍历所有哈希代码之后,您将耗尽哈希代码。

具有相同哈希码的两个字符串称为冲突。对于一个好的哈希码,您可以考虑两个无辜字符串的概率为1 /#{哈希空间的大小}。

另一个不错的属性是,您希望任何截断您的哈希码的行为都是这样的。截断哈希码实际上是正确的做法。

您将获得两个给定名称的碰撞概率约为1 / 16M。 但是,如果你有N字符串,你可能会遇到所谓的birthday problem。 观察至少1次碰撞的概率要大得多。接近N ^ 2 / 16M。

答案 3 :(得分:0)

两个想法:

1)如果在开始着色之前已知所有N个字符串,请计算Perfect HashCC#中的实现)。然后,您可以将每个哈希值乘以256 ^ 3 / N,以在颜色空间上展开结果。

2)如果不知道它们,您可以手动解决冲突。像这样的伪代码:

tries = 5; //arbitrary number       
colorcode = myString.GetHashCode()&0xFFFFFF;
while ( Dictionary.containsKey(colorcode ) && 
        Dictionary.getValue(colorcode )!= myString &&
        tries-->0) {
    colorcode = ((colorcode+3) * 92821) &0xFFFFFF;  //rehash
}
if (tries) {
    Dictionary.insert(colorcode , myString);
} 
rectangleBackColor = Color.FromArgb(0xFF000000|colorcode );