Случайность Python random
Я использую Python для генерации изображений, используя пунктирные линии для пунктировки. Период штрихов является постоянным, что меняет соотношение штрих/пространство. Это производит что-то вроде этого:
Однако на этом изображении штрих имеет равномерное происхождение, и это создает неприглядные вертикальные желоба. Поэтому я попытался рандомизировать происхождение, чтобы удалить желоба. Это работает, но есть очевидная закономерность:
Интересно, откуда это взялось, я сделал очень простой тестовый пример со сложенными пунктирными прямыми:
- коэффициент тире: 50%
- тире 20px
- смещение начала координат от -10px до +10px с использованием
random.uniform(-10.,+10.)
(*) (после начального random.seed()
И с добавленной случайностью:
Так что есть еще шаблон. Чего я не понимаю, так это того, что для получения видимого желоба необходимо, чтобы 6 или 7 последовательных значений попадали в один и тот же диапазон (скажем, половина общего диапазона), что должно составлять 1/64 вероятности, но, похоже, происходит много чаще всего в 200 сгенерированных строках.
Я что-то неправильно понимаю? Это просто наш человеческий мозг, который видит шаблоны там, где их нет? Может ли быть лучший способ генерировать что-то более "визуально случайное" (python 2.7 и желательно без установки чего-либо)?
(*) частичные пиксели действительны в этом контексте
Приложение: код, который я использую (это скрипт Gimp):
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
# Python script for Gimp (requires Gimp 2.10)
# Run on a 400x400 image to see something without having to wait too much
# Menu entry is in "Test" submenu of image menubar
import random,traceback
from gimpfu import *
def constant(minShift,maxShift):
return 0
def triangle(minShift,maxShift):
return random.triangular(minShift,maxShift)
def uniform(minShift,maxShift):
return random.uniform(minShift,maxShift)
def gauss(minShift,maxShift):
return random.gauss((minShift+maxShift)/2,(maxShift-minShift)/2)
variants=[('Constant',constant),('Triangle',triangle),('Uniform',uniform),('Gauss',gauss)]
def generate(image,name,generator):
random.seed()
layer=gimp.Layer(image, name, image.width, image.height, RGB_IMAGE,100, LAYER_MODE_NORMAL)
image.add_layer(layer,0)
layer.fill(FILL_WHITE)
path=pdb.gimp_vectors_new(image,name)
# Generate path, horizontal lines are 2px apart,
# Start on left has a random offset, end is on the right edge right edge
for i in range(1,image.height, 2):
shift=generator(-10.,10.)
points=[shift,i]*3+[image.width,i]*3
pdb.gimp_vectors_stroke_new_from_points(path,0, len(points),points,False)
pdb.gimp_image_add_vectors(image, path, 0)
# Stroke the path
pdb.gimp_context_set_foreground(gimpcolor.RGB(0, 0, 0, 255))
pdb.gimp_context_set_stroke_method(STROKE_LINE)
pdb.gimp_context_set_line_cap_style(0)
pdb.gimp_context_set_line_join_style(0)
pdb.gimp_context_set_line_miter_limit(0.)
pdb.gimp_context_set_line_width(2)
pdb.gimp_context_set_line_dash_pattern(2,[5,5])
pdb.gimp_drawable_edit_stroke_item(layer,path)
def randomTest(image):
image.undo_group_start()
gimp.context_push()
try:
for name,generator in variants:
generate(image,name,generator)
except Exception as e:
print e.args[0]
pdb.gimp_message(e.args[0])
traceback.print_exc()
gimp.context_pop()
image.undo_group_end()
return;
### Registration
desc="Python random test"
register(
"randomize-test",desc,'','','','',desc,"*",
[(PF_IMAGE, "image", "Input image", None),],[],
randomTest,menu="<Image>/Test",
)
main()
Ответы
Ответ 1
Думайте об этом так: желоб воспринимается, пока он не заблокирован (или почти так). Это происходит только тогда, когда две последовательные линии почти полностью не в фазе (черные сегменты в первой линии лежат чуть выше белых сегментов в следующей). Такие экстремальные ситуации случаются только в одном из каждых 10 рядов, следовательно, видимые желоба, которые, кажется, расширяются примерно на 10 рядов, прежде чем будут заблокированы.
Посмотрите на это по-другому - если вы распечатываете изображение, на самом деле есть длинные белые каналы, по которым вы можете легко нарисовать линию пером. Почему ваш разум не должен их воспринимать?
Чтобы получить лучшую визуальную случайность, найдите способ сделать последовательные линии зависимыми, а не независимыми, таким образом, чтобы поведение почти в противофазе появлялось чаще.
Ответ 2
Есть, по крайней мере, одна очевидная причина, по которой мы видим шаблон на "случайной" картинке: 400x400 пикселей - это те же самые 20x400 пикселей, которые повторяются 20 раз.
Таким образом, каждое видимое движение повторяется 20 раз параллельно, что действительно помогает мозгу анализировать картину.
На самом деле, один и тот же шаблон шириной 10px повторяется 40 раз, чередуя черный и белый:
Вы можете рандомизировать период тире отдельно для каждой строки (например, между 12 и 28):
Вот соответствующий код:
import numpy as np
import random
from matplotlib import pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = [13, 13]
N = 400
def random_pixels(width, height):
return np.random.rand(height, width) < 0.5
def display(table):
plt.imshow(table, cmap='Greys', interpolation='none')
plt.show()
display(random_pixels(N, N))
def stripes(width, height, stripe_width):
table = np.zeros((height, width))
cycles = width // (stripe_width * 2) + 1
pattern = np.concatenate([np.zeros(stripe_width), np.ones(stripe_width)])
for i in range(height):
table[i] = np.tile(pattern, cycles)[:width]
return table
display(stripes(N, N, 10))
def shifted_stripes(width, height, stripe_width):
table = np.zeros((height, width))
period = stripe_width * 2
cycles = width // period + 1
pattern = np.concatenate([np.zeros(stripe_width), np.ones(stripe_width)])
for i in range(height):
table[i] = np.roll(np.tile(pattern, cycles), random.randrange(0, period))[:width]
return table
display(shifted_stripes(N, N, 10))
def flexible_stripes(width, height, average_width, delta):
table = np.zeros((height, width))
for i in range(height):
stripe_width = random.randint(average_width - delta, average_width + delta)
period = stripe_width * 2
cycles = width // period + 1
pattern = np.concatenate([np.zeros(stripe_width), np.ones(stripe_width)])
table[i] = np.roll(np.tile(pattern, cycles), random.randrange(0, period))[:width]
return table
display(flexible_stripes(N, N, 10, 4))
Ответ 3
Это немного нелогично, но при добавлении случайных элементов случайность становится меньше. Если я правильно следую, диапазон каждого элемента составляет 10 - 30 пикселей. Таким образом, общий размер 10 элементов составляет от 100px до 300px, но распределение даже не распространяется на этот диапазон. Крайности очень маловероятны, и в среднем они будут довольно близки к 200px, так что появится фундаментальный шаблон 20px. Ваше случайное распределение должно избежать этого.
РЕДАКТИРОВАТЬ: я вижу, что я немного неправильно понял, и все тире 20px со случайным смещением. Итак, я думаю, что просмотр любого 1 вертикального набора штрихов будет казаться случайным, но этот же случайный набор повторяется по всей странице, давая шаблон.
Ответ 4
Публикация моего окончательного решения в качестве ответа, но, пожалуйста, опишите других.
Джон Колман имеет точку, когда он говорит:
Чтобы получить лучшую визуальную случайность, найдите способ сделать последовательные линии зависимыми, а не независимыми, таким образом, чтобы поведение почти в противофазе появлялось чаще.
Итак, наконец, лучший способ избежать желобов - это избежать случайности и иметь очень фиксированную схему сдвигов, и хорошо работает 4-фазный цикл 0,25%, 75%, 50%:
Хорошо, еще есть небольшой ромбовидный узор, но он гораздо менее заметен, чем узоры, представленные случайными схемами, которые я пробовал.
Ответ 5
Перейдя по ссылке, размещенной в комментариях, можно найти полезную информацию, поскольку я делаю вид, что пишу что-то, связанное с ней,
Что касается несколько реализаций методы случайной() вы можете найти в некоторых библиотеках питона, как NumPy, случайные механические красиво объяснены здесь, и он говорит:
(pseudo-) случайные числа работают, начиная с числа (начального числа), умножая его на большое число, а затем беря по модулю этого произведения. Полученное число затем используется в качестве начального числа для генерации следующего "случайного" числа. Когда вы устанавливаете семя (каждый раз), оно делает то же самое каждый раз, давая вам одинаковые числа.
Вместо этого, как сказано в ссылочной ссылке, понимая Twister Mersenne, вы можете наблюдать случайный паттерн случайным образом, чтобы предсказать следующий элемент в последовательности, зная немного информации. Тем не менее, немного странно материализовать это наблюдение, посмотрев прямо на изображения, которые вы генерируете, прекрасный пример случайности.
Я считаю, что переход на случайную криптоуровню может быть хорошим шансом для вашей цели, возможно, может помочь чтение контента, похожего на эту, о лучших случайных вещах в python.
Это часть всей истории, укажет нам на библиотеку uuid
разработанную, чтобы "гарантировать уникальность в пространстве и времени".
import uuid
>> uuid.uuid4()
UUID('e06bea9b-6cf6-46a0-8438-f61497409193')
>> a=uuid.uuid4()
>> a.bytes
b'\xdal\xdc\xc8\xbe\x15I\x06\xba\xfa {\xd3!'\xf7'
Вы можете использовать информацию из uuid4()
для создания отступов между слешами.
Ответ 6
Это хороший пример того, почему настоящий ГСЧ стоит сотни или тысячи долларов. Если вы хотите истинную случайность, вам нужно использовать квантовый источник. Есть хорошие процедуры, генерирующие псевдослучайные числа, но это требует больше усилий, чем вызов встроенных функций для их генерации.
Я бегу симуляции Монте-Карло, где случайность является ключевым. У нас есть самописный ГСЧ (не мной), который генерирует тот же набор случайных чисел на основе начального числа семян, с которого мы начинаем. Результат довольно случайный, но набор всегда одинаков, если начальная точка одинакова. Я рекомендую искать алгоритм, кроме встроенных функций.