Python-获取随机颜色,并尽可能快地获得种子数

时间:2018-10-10 14:50:27

标签: python python-3.x random hash

给定一个特定的种子编号,我需要找到一种随机的颜色-快速。 给定两次相同的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)应该与id_to_random_color(9)完全不同

3 个答案:

答案 0 :(得分:3)

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

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的评论中所述,如果在非连续的情况下重复输入,该函数可能返回不同的值。
这是一个可能的解决方法:

import random
memory = {}
def id_to_random_color(number, memory):
    if not number in memory:
        r = random.random()
        g = random.random()
        b = random.random()
        memory[number] = (r, g, b, 1.0)
    return memory[number]

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

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在其回答中所述)。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

您没有提到number的范围。它必须是一个非负整数,否则bytes(number)会失败。 (顺便说一句,该函数返回一个bytes字符串,其中包含number个零字节,如果number大,则会占用大量RAM)。我假设number至少为24位,以覆盖24位RGB颜色空间。

为此目的使用加密哈希函数是过大的。 OTOH,hashlib函数非常快,因为它们是用C编码的。我们可以利用内置的hash函数,但是hash(n)只是返回{{ 1}}用于机器大小的整数,因此我们需要进行类似n的操作才能获得看起来随机的输出。但是,执行这种操作的结果并不是特别随机:hash((n, n))是专为哈希表设计的,而不是我们想要的加扰类型。

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

hash

此功能在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] 中的n上很好用,实际上,其结果在整个range(2**24)上都是不错的;仍然会在该范围之外提供可用的结果。为了在这里进行测试,我将使用简化版本,该版本将RGB值返回为整数。第一个测试只是显示range(2**32)n的RGB值。第二个测试生成25600个随机数,并找到相应的RGB值。每个R,G和B值大约应获得100次匹配。

range(20)

输出

from collections import Counter
from random import seed, randrange

seed(42)

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

# Tests

# Show some colors
for i in range(20):
    rgb = id_to_RGB(i)
    print('{:2d}: {:02x} {:02x} {:02x}'.format(i, *rgb))
print()

# Count the frequency of each color for random `n`
counts = {k: Counter() for k in 'rgb'}
for i in range(25600):
    n = randrange(2 ** 32)
    for k, v in zip('rgb', id_to_RGB(n)):
        counts[k][v] += 1

for k in 'rgb':
    print(k, sorted(counts[k].values()))

计数器输出

 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] 为零输入返回所有零。如果不希望这样做,则可以在开始时添加一个额外的混合步骤(也可以从xxHash借用)。

id_to_RGB