Python获得随机颜色,给定种子数的速度尽可能快

2024-05-19 10:54:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我需要找到一个随机的颜色,给定一个特定的种子数-快。 给定相同的ID两次,应该返回相同的颜色。在

我这样做了:

def id_to_random_color(number):
    random_bytes = hashlib.sha1(bytes(number)).digest()
    return [int(random_bytes[-1]) / 255, int(random_bytes[-2]) / 255, int(random_bytes[-3]) / 255, 1.0]

问题是,多次计算数字的sha1总的来说非常慢。(我使用这个函数大约10万次)

编辑:我使用散列函数的原因是,我希望对接近的数字使用不同的颜色

例如id_to_random_color(7)应该与{}非常不同


Tags: to函数idnumberbytes颜色def数字
3条回答

你没有提到number的范围。它必须是非负整数,否则bytes(number)将失败。(顺便说一句,该函数返回一个由number个零字节组成的bytes字符串,如果number很大,它将占用大量的RAM)。我假设number至少是24位来覆盖24位RGB颜色空间。在

为此目的使用加密哈希函数是过分的。因此,hashlib函数非常快,因为它们是用C语言编写的。我们可以使用内置的函数,但是对于机器大小的整数,hash(n)只返回{},所以我们需要做类似hash((n, n))的操作来获得随机输出。然而,做这种事情的结果并不是特别随机的:hash是为哈希表工作而设计的,而不是我们这里想要的那种置乱。在

为了生成随机的RGB值,我采用了Yann Collet的xxHash的混合算法。您可以在xxhash.c source code中查看该算法的C源代码。该算法速度相当快,具有良好的avalanching。Bret Mulvey写了一篇很好的关于hash mixing functions and the avalanche effect的介绍性文章。在

def id_to_random_color(n):
    n = ((n ^ n >> 15) * 2246822519) & 0xffffffff
    n = ((n ^ n >> 13) * 3266489917) & 0xffffffff
    n = (n ^ n >> 16) >> 8
    return [u / 255. for u in n.to_bytes(3, 'big')] + [1.0]

此函数适用于range(2**24)中的n,事实上,它的结果在整个range(2**32)中是相当好的;它仍将在该范围之外给出有用的结果。为了在这里测试它,我将使用一个简化的版本,它将RGB值作为整数返回。第一个测试只显示nn的RGB值。第二个测试生成25600个随机数并找到相应的RGB值。每个R、G和B值应该可以得到大约100次点击。在

^{pr2}$

输出

 0: 00 00 00
 1: 60 6d 18
 2: 4e f2 bf
 3: 75 4f 48
 4: 60 98 f1
 5: 17 1d 98
 6: 3b 69 13
 7: aa 10 98
 8: c1 31 e3
 9: 1e fa 4a
10: 7f 05 b2
11: 86 0e b3
12: 39 84 c6
13: c1 75 4f
14: e2 38 87
15: db 54 79
16: 45 14 b6
17: cb 56 68
18: 8e bf d8
19: cd 50 3f

计数器输出

r [74, 75, 75, 77, 78, 80, 80, 80, 80, 81, 82, 83, 84, 85, 85, 85, 86, 86, 86, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110, 110, 110, 110, 110, 110, 110, 111, 112, 112, 112, 112, 112, 113, 113, 113, 114, 114, 115, 115, 115, 115, 116, 116, 116, 116, 118, 119, 120, 123, 124, 126, 128, 138]
g [73, 74, 74, 77, 78, 79, 79, 81, 81, 82, 82, 83, 83, 83, 84, 84, 84, 84, 85, 85, 85, 86, 87, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 109, 109, 109, 110, 110, 110, 110, 110, 111, 111, 111, 111, 111, 111, 112, 112, 112, 112, 112, 112, 113, 113, 113, 113, 113, 113, 113, 113, 114, 114, 114, 114, 115, 115, 116, 117, 117, 117, 117, 118, 118, 118, 119, 119, 119, 120, 120, 121, 121, 121, 123, 125, 126, 128]
b [73, 74, 77, 78, 78, 79, 80, 80, 80, 81, 82, 84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 87, 87, 87, 87, 88, 88, 89, 89, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 92, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 110, 110, 110, 111, 111, 111, 111, 112, 112, 112, 113, 113, 113, 114, 114, 114, 114, 114, 115, 115, 115, 115, 115, 115, 115, 115, 115, 116, 116, 116, 117, 118, 119, 120, 120, 122, 124, 126, 127, 128, 131]

您可能会注意到,id_to_RGB对于一个零输入返回所有的零。如果这是不需要的,您可以在开始处添加一个额外的混合步骤(也借用了xxHash)。在

def id_to_RGB(n):
    n = (374761397 + n * 3266489917) & 0xffffffff    
    n = ((n ^ n >> 15) * 2246822519) & 0xffffffff
    n = ((n ^ n >> 13) * 3266489917) & 0xffffffff
    n = (n ^ n >> 16) >> 8
    return tuple(n.to_bytes(3, 'big'))

使用带有静态变量的简单随机数生成器可以提高性能:

import random
prev, r, g, b = None, 0, 0, 0
def id_to_random_color(number):
    global prev, r, g, b
    if number != prev:
        r = random.random()
        g = random.random()
        b = random.random()
        prev = number
    return r, g, b, 1.0

更新:
正如AndrewMcDowell在其评论中所说,如果输入在不连续的情况下重复,函数可能会返回不同的值。
以下是一个可能的解决方法:

^{pr2}$

进一步更新:
甚至可以使用相同的函数骨架来计算哈希:

memory = {}
def id_to_random_color(number):
    if not number in memory:
        numByte = str.encode(number)
        hashObj = hashlib.sha1(numByte).digest()
        r, g, b = hashObj[-1] / 255.0, hashObj[-2] / 255.0, hashObj[-3] / 255.0
        memory[number]= (r, g, b, 1.0)
        return r, g, b, 1.0
    else:
        return memory[number]

尽管语法有点冗长,但是else语句提高了位性能,避免了随后的内存写入和读取(正如Jake在回答中所述)。在

我会用字典快速索引已经生成的种子。在

import random

random_seeds = {}

def id_to_random_color(number):
    if number in random_seeds.keys():
        return random_seeds[number]
    else:
        color = [random.random(), random.random(), random.random(), 1.0]
        random_seeds[number] = color
        return color

相关问题 更多 >

    热门问题