Я видел этот генератор псевдослучайных чисел для использования в шейдерах, упоминаемых здесь и там в сети :
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
Его по-разному называют «каноническим» или «однострочным, который я где-то нашел в Интернете».
Каково происхождение этой функции? Являются ли постоянные значения столь же произвольными, как кажутся, или их выбор является искусством? Есть ли обсуждение достоинств этой функции?
РЕДАКТИРОВАТЬ: самая старая ссылка на эту функцию, с которой я столкнулся, - это архив от февраля 2008 года , исходная страница теперь удалена из Интернета. Но там это обсуждается не больше, чем где-либо еще.
Ответы:
Очень интересный вопрос!
Я пытаюсь понять это, набирая ответ :) Сначала простой способ поиграть с ним: http://www.wolframalpha.com/input/?i=plot%28+mod%28+sin%28x*12.9898 +% 2B + y * 78.233% 29 + * + 43758.5453% 2C1% 29x% 3D0..2% 2C + y% 3D0..2% 29
Затем давайте подумаем, что мы здесь пытаемся сделать: для двух входных координат x, y мы возвращаем «случайное число». Но это не случайное число. То же самое каждый раз, когда мы вводим одни и те же x, y. Это хеш-функция!
Первое, что делает функция, - это переход от 2d к 1d. Само по себе это неинтересно, но числа выбраны так, чтобы они обычно не повторялись. Также у нас есть добавление с плавающей запятой. Будет еще несколько бит от y или x, но числа могут быть просто выбраны правильно, чтобы получилось смешивание.
Затем мы выбираем функцию черного ящика sin (). Это будет во многом зависеть от реализации!
Наконец, он усиливает ошибку в реализации sin () путем умножения и взятия дроби.
Я не думаю, что это хорошая хеш-функция в общем случае. В числовом выражении sin () - это черный ящик на графическом процессоре. Должно быть возможно построить гораздо лучший, взяв почти любую хеш-функцию и преобразовав ее. Сложнее всего превратить типичную целочисленную операцию, используемую при хешировании процессора, в операции с плавающей запятой (половинной или 32-битной) или операции с фиксированной точкой, но это должно быть возможно.
Опять же, настоящая проблема с этой хеш-функцией заключается в том, что sin () - это черный ящик.
источник
Источником, вероятно, является статья: «О генерации случайных чисел с помощью y = [(a + x) sin (bx)] mod 1», WJJ Rey, 22-е Европейское совещание статистиков и 7-я Вильнюсская конференция по теории вероятностей и Математическая статистика, август 1998 г.
РЕДАКТИРОВАТЬ: Поскольку я не могу найти копию этой статьи, а ссылка на «TestU01» может быть непонятной, вот схема, описанная в TestU01 на псевдо-C:
#define A1 ??? #define A2 ??? #define B1 pi*(sqrt(5.0)-1)/2 #define B2 ??? uint32_t n; // position in the stream double next() { double t = fract(A1 * sin(B1*n)); double u = fract((A2+t) * sin(B2*t)); n++; return u; }
где единственное рекомендуемое постоянное значение - B1.
Обратите внимание, что это для потока. Преобразование в одномерный хэш 'n' становится целочисленной сеткой. Итак, я предполагаю, что кто-то видел это и преобразовал 't' в простую функцию f (x, y). Используя исходные константы, указанные выше, мы получим:
float hash(vec2 co){ float t = 12.9898*co.x + 78.233*co.y; return fract((A2+t) * sin(t)); // any B2 is folded into 't' computation }
источник
fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (co.xy + vec2(43758.5453, SOMENUMBER))
чтобы соответствовать функции, о которой идет речь.a
иb
), используемых снова и снова, остается, но, возможно, они использовались в статье, которую вы цитируете.постоянные значения произвольны, особенно если они очень большие и отличаются от простых чисел на пару десятичных знаков.
модуль более 1 синуса высокой амплитуды, умноженный на 4000, является периодической функцией. это как оконная штора или гофрированный металл, сделанный очень маленьким, потому что он умножается на 4000 и повернут под углом скалярным произведением.
поскольку функция двумерная, скалярное произведение имеет эффект поворота периодической функции под наклоном относительно осей X и Y. Примерно в соотношении 13/79. Это неэффективно, вы действительно можете добиться того же, выполнив синус (13x + 79y), это также даст то же самое, я думаю, с меньшим количеством математики ..
Если вы найдете период функции как по X, так и по Y, вы можете сделать выборку, чтобы она снова выглядела как простая синусоида.
Вот картина его увеличенная в графике
Я не знаю происхождения, но он похож на многие другие, если вы будете использовать его в графике через регулярные промежутки времени, он будет иметь тенденцию создавать муаровые узоры, и вы можете видеть, что он в конечном итоге повторяется снова.
источник
(13x + 79y)
потому что онdot(XY, AB)
будет делать именно то, что вы описываете, как скалярное произведение, котороеx,y dot 13, 79 = (13x + 79y)
Может быть, это какое-то одноразовое хаотическое отображение, тогда оно могло бы многое объяснить, но также может быть просто произвольной манипуляцией с большими числами.
РЕДАКТИРОВАТЬ: В основном функция фракт (sin (x) * 43758.5453) представляет собой простую хеш-подобную функцию, sin (x) обеспечивает плавную интерполяцию sin от -1 до 1, поэтому sin (x) * 43758.5453 будет интерполяцией из - 43758.5453 к 43758.5453. Это довольно большой диапазон, поэтому даже небольшой шаг по x обеспечит большой шаг в результате и действительно большое изменение дробной части. «Фракт» нужен для получения значений в диапазоне от -0,99 ... до 0,999 .... Теперь, когда у нас есть что-то вроде хеш-функции, мы должны создать функцию для производственного хеша из вектора. Самый простой способ - это вызвать "хеш" отдельно для x любой компоненты y входного вектора. Но тогда у нас будут некоторые симметричные значения. Итак, мы должны получить некоторое значение из вектора, подход состоит в том, чтобы найти некоторый случайный вектор и найти "точечный" продукт на этот вектор, вот и мы: фракт (sin (точка (co.xy, vec2 (12.9898,78.233))) * 43758.5453); Также, в соответствии с выбранным вектором, его длина должна быть длинной, чтобы было несколько периодов функции «sin» после вычисления «скалярного» произведения.
источник