Flash - статьи

Немного оптимизации: истребляем в себе все человеческое


Еще чуть-чуть теории — а потом сплошная практика. Поговорим о человеческом факторе в науке.

Если порыться в любой научной отрасли, даже в сравнительно чистой математике или физике, можно найти не только штаны Пифагора, но и старые носки Эйлера. Например, та же десятеричная система появилась благодаря привычке считать на пальцах. Ну разве не абсурдная причина? Та же секунда времени — усредненная частота сердечного ритма. Ну разве не странная единица? Более поздние "находки", типа 360 градусов окружности — это уже дань более ученым расчетам. Не смогли поделить угол на три части, вот и придумали: сделаем в полном развороте 3х4х5х6=360 частей, будет делиться по определению на что угодно.

С другой стороны есть реально интересные числа: то же. Например, стоит договориться с тараканами, что такое окружность, ее центр и радиус, а также выяснить, что такое ряды, так сразу любой таракан сможет доказать вам, что длина окружности, выраженная через ее же радиус, равна Пи.

Короче, старая хитрость: хочешь понять систему — говори на ее языке, а не на своем. Это знают математики и программисты, взять ту же задачу вавилонских башен и ее рекурсивное решение. Оказалось, что есть простое решение, нужно только выбрать точку зрения.

А мы тут при чем? Вот при чем. Как правило, при задании dt в числовых методах используются "отбалденные" значения типа 0,1. Это число просто удобно в десятичной системе счисления — больше в нем ничего хорошего. Даже если применяются методы динамического шага, то в лучшем случае мы перейдем к 0,2 или 0,01. Хрен редьки не слаще.

На самом-то деле наша система обладает своим собственным сердцем: R*cos(wt). Вот это и есть естественный для нашей системы хронометр, часы — или называйте как хотите. Один полный оборот этих часов — это и будет наша единица времени, назовем ее революцией, то есть оборотом.

По ходу задачи для получения аттракторов мы должны будем фиксировать значения в одной фазе нашего колебатора, например в нулевой. Если мы выберем dt произвольно, то не факт, что в одном обороте будет целое число шагов — даже наверняка не будет. В результате наш рабочий цикл для вычисления тысячи точек будет выглядеть примерно так:

int time=0; dt=0.1; cnt=0;
while(cnt<1000) {
x1=fn(x);
    if ((2*Pi*(cnt+1))-(tme*w))>=0 {cnt++; … }
    x=x1;
}


Короче, мы должны будем отслеживать, как наша "стрелка часов" приближается к нулевой отметке, и брать, например, первую точку, которая прошла "за полночь". Не точно, не удобно и не спортивно. Гораздо проще и куда интереснее разделить нашу революцию на целое число частей (как это сделано с градусами) и "ходить конем" только в эти точки. Тогда вычисления аттрактора будут точными, кратными целому и постоянному числу "ходов".

На сколько частей делить революцию — априори не понятно, но можно предположить, что для вычислений удобными будут степени двойки. Тогда мы сможем для энного хода получать значение mod(n), что для степеней двойки эффективно реализуется отбрасыванием старших разрядов с помощью битовой операции AND.

Введем такую величину, как революция/2n, или сокращенно revN2. Это величина, представляющая деление одного оборота на 2n. То есть: rev8 — 256-я часть от одного оборота, rev10 — 1024-я и т.д. Такая система измерения удобна для компьютера. Например, можно создать объект rev (8, "сos", R, w), который просчитывает косинус с градацией 256 позиций на один оборот, с радиусом R и частотой w и заносит результаты в таблицу-массив. Частота тут, собственно, нужна, для вычисления временного шага: если rev8=2*Pi/2n, то w*dt_revN=2*Pi/2n, в частности w*dt_rev0=2*Pi/20. Это можно реализовать как метод объекта rev, например GetTimeStep().

Теперь все чудно: для получения тысячи точек аттрактора только и нужно, что устроить цикл:

for (i=0;i++;i<1000) for (j=0;j++;j<256)

А значения косинуса можно получать из массива в заранее просчитанном объекте rev:

rev.GetValue(i)

Не буду рассказывать, насколько выборка из массива быстрее вычисления косинуса. Скажу только, что в ActiveScript обе операции реализованы способом, далеким от идеального. Массивы на самом деле представляют собой ассоциативные списки Java, косинус — тоже, очевидно, не реализуется одной процессорной командой, как могло бы быть в идеале. Но и в этом случае массив дает огромное преимущество, включая экономию на одном дополнительном умножении на радиус на каждом расчете. Ситуация осложняется еще и тем, что для расчета одной точки Cos вызывается четыре раза, то есть для тысячи точек и rev8 количество вычисляемых косинусов достигает 4х256х1000=1e6, то есть одного миллиона! Это не шутка даже для ассемблера.

На этом — конец теории, полный и окончательный.


Содержание раздела