Набор Мандельброта достигает предела слишком рано - PullRequest
1 голос
/ 01 февраля 2012

Я вычисляю набор Мандельброта с возможностью масштабирования и печати его на экране с помощью OpenGL.

как вы знаете, набор Мандельброта определяется прямоугольником (верхняя правая точка и нижняя левая точка), и каждый раз, когда я «увеличиваю» и «уменьшаю», я меняю следующие точки, перемещая нижнюю левую точку в верхний правый угол. сторона и верхняя правая точка наоборот. в конечном итоге эти 2 точки должны встретиться и завершить процесс масштабирования.
по многим фильмам YOUTUBE я ясно вижу, что некоторые фильмы позволяют увеличить зум до 1000 раз. когда в моей программе я едва могу добраться до X6. во время отладки я вижу, как мои 2 точки, которые определяют набор Мандельброта, достигают друг друга (x1,y1) = (x2,y2).
Несколько вещей:
(x1,y1) & (x2,y2) определены как числа с плавающей запятой. я должен использовать двойной вместо этого? мой Мандельброт определяется (512X512) баллов. Этого достаточно? хотя я не уверен, что это связано с проблемой.

Другая проблема, с которой я сталкиваюсь, заключается в том, что я печатаю комплект как карту высот (комплект 3D). когда каждый компонент Y представляет количество итераций, для достижения бесконечности требуется определенная точка. однако каждый раз, когда я увеличиваю масштаб всего набора, он перемещается сверху и снизу к позициям моей камеры, и в конечном итоге моя камера поглощается этим набором. Есть ли способ рассчитать разницу и отодвинуть камеру от набора (соответственно, от точки увеличения?)

код, который вычисляет набор:

double ratiox = instance->foundPointOnHost.x / ((instance->constVal[1][0] - instance->constVal[0][0]));;
            double ratioy = 1-instance->foundPointOnHost.z / ((instance->constVal[1][1] - instance->constVal[0][1]));;
            double xrange = instance->m_GraphConfig.xru-instance->m_GraphConfig.xld;
            double yrange = instance->m_GraphConfig.yru-instance->m_GraphConfig.yld;
            instance->m_GraphConfig.xld += 5*direction*0.005*ratiox*xrange;
            instance->m_GraphConfig.xru -= 5*direction*0.005*(1.-ratiox)*xrange;
            instance->m_GraphConfig.yld += 5*direction*0.005*(1.-ratioy)*yrange;
            instance->m_GraphConfig.yru -= 5*direction*0.005*ratioy*yrange;  

несколько вещей:

instance-> FoundPointOnHost = точка, которую я хочу увеличить.
instance-> constVal = содержит исходный размер набора (в начале равен [xru, yru] [xld, yld])
(xru, yru) = верхние правые точки множества (xld, yld) = нижние левые точки множества

спасибо!

Ответы [ 2 ]

1 голос
/ 02 февраля 2012

Спасибо, что включили ваш код! (+1 к вопросу об уточнении и включении всей соответствующей информации).

Это показывает, что ваша теория масштабирования верна. На каждом шаге xrange и yrange будут масштабироваться с постоянным коэффициентом (0,975?), И окно будет двигаться в направлении foundPointOnHost, сохраняя одинаковое относительное положение в окне (ratioX и ratioY) , Это определенно правильная теория за масштабирование. Однако на практике реализация масштабирования приведет к быстрому накоплению ошибок округления из-за способа работы арифметики с плавающей запятой.

Мои эмпирические правила:

  • каждый раз, когда вы умножаете или делите два числа с плавающей запятой, у вас есть шанс ввести ошибку, размер которой равен последнему биту результата ;
  • но каждый раз, когда вы добавляете или вычитаете два числа с плавающей запятой, у вас есть шанс ввести ошибку, размер которой равен последний бит самого большого операнда .

Сработал пример. Предположим, вы увеличиваете масштаб (0,5,0,3), и вы уже увеличены, поэтому xrange и yrange составляют около 0,001. Допустим, вы используете числа с плавающей запятой (23 бита, около 7 знаков после запятой). В вашем коде, когда вы вычитаете два x значения для пересчета xrange, вы вычитаете два числа, оба из которых близки к 0,5, что означает, что ваша ошибка в xrange составляет целых 0,0000001 - седьмое десятичное место 0,5 - и так как результат составляет около 0,001, ваш xrange только с точностью до четырех знаков после запятой. Затем вы масштабируете, масштабируете и добавляете xrange обратно к краям окна (на величину, которая сейчас составляет около 0,00025), что снова составляет около 0,5, так что арифметическая операция также точна только до 0,0000001, и окно Сами ребра неточны по сравнению с предыдущим вызовом. Очевидно, что лучше, если вы используете double, но, опять же, ваш xrange теряет точность каждый раз в цикле; и к тому времени, как вы достигнете уровня зуммирования x6, возможно, вы потеряли все это.

«Хитрость» заключается в том, чтобы уменьшить количество сложений и вычитаний (в настоящее время вы делаете 6 шагов масштабирования) и следите за тем, чтобы ваши ошибки не накапливали на каждой итерации. Я бы предложил что-то вроде следующего: оставьте xrange и yrange и пересчитывайте края каждый шаг: (ratioX и ratioY являются постоянными, и вам, возможно, придется определить, какой из них ratio и который 1-ratio:)

xrange *= 0.975;
xld = foundPointOnHost.x - ratioX * xrange;
xru = foundPointOnHost.x + (1-ratioX) * xrange;

и то же самое для y. Более того, вычислите xrange, используя операцию, которая сама по себе не будет накапливать ошибки при каждом умножении:

// xrange = INITIAL_RANGE * SCALE_FACTOR^frame_number
xrange = exp ( log(INITIAL_RANGE) + frame_number*log(SCALE_FACTOR) );
1 голос
/ 01 февраля 2012

Да, если вы можете использовать double вместо float.

Число с плавающей запятой имеет только 24 бита мантиссы, и при умножении значений на предел точности снова и снова на дополнительноегенерируемые биты просто теряются.

FWIW, мой генератор mandelbrot WebGL (который ограничен float из-за WebGL) действительно справляется с увеличением в 5000 раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...