Вы не упомянули диапазон number
.Это должно быть неотрицательное целое число, иначе bytes(number)
потерпит неудачу.(Кстати, эта функция возвращает строку bytes
, состоящую из number
нулевых байтов, которая потратит много оперативной памяти, если number
велико).Я предполагаю, что number
составляет не менее 24 бит, чтобы покрыть 24-битное цветовое пространство RGB.
Использование криптографической хеш-функции для этой цели является излишним.OTOH, функции hashlib
довольно быстрые, так как они кодируются на C. Мы могли бы использовать функцию built_in hash
, однако hash(n)
просто возвращает n
для целых чисел машинного размера,поэтому нам нужно сделать что-то вроде hash((n, n))
, чтобы получить случайный результат.Однако результаты такого рода действий не являются случайными: hash
предназначен для работы с хеш-таблицами, а не для скремблирования, которое мы хотим здесь.
Для генерации случайных значений RGB я адаптировал алгоритм микширования из xxHash от Yann Collet.Вы можете просмотреть исходный код C этого алгоритма в исходном коде xxhash.c .Этот алгоритм достаточно быстрый и имеет хорошее лавинное .Брет Малви написал хорошую вводную статью о функциях смешивания хэшей и лавинном эффекте .
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)
, и на самом деле ее результатыдовольно хороши в целом range(2**32)
;это все еще даст полезные результаты за пределами этого диапазона.Чтобы проверить это здесь, я буду использовать упрощенную версию, которая возвращает значения RGB в виде целых чисел.Первый тест просто показывает значения RGB для n
в range(20)
.Второй тест генерирует 25600 случайных чисел и находит соответствующие значения RGB.Мы должны получить примерно 100 совпадений для каждого значения R, G и B.
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]
Вы можете заметить, что 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'))