Присоединяйтесь к сообществу форекс трейдеров
прямо сейчас!
Научитесь зарабатывать и приумножать деньги
Расскажите о своем опыте торговли
Получите ответы на любые вопросы от специалистов
Знакомьтесь и общайтесь с лидерами рынка
Читайте актуальную и закрытую информацию и многое другое...
Функции перевода чисел между десятичной и двоичной системами - Сообщество успешных трейдеров
Недавно писал скрипт для заказчика, и мне понадобилась функция перевода двоичных данных в десятичные. Сначала я попробовал решить эту задачу "в лоб":
Code
int BinToInt(string s) { int i; int res = 0; int l = StringLen(s); for (i = 0; i < l; i++) if (StringGetChar(s, i) == '1') res += MathPow(2, l - i - 1); return(res); }
Коротко опишу приведенный код. На входе мы получаем двоичное число в виде строки, состоящей из нолей и единиц. В переменную l мы получаем длину этой строки и пробегаемся по ней в цикле с итератором i, сверяя каждый раз i-тую позицию строки, является ли она единицей. Если является, то к результирующей переменной res, изначально проинициализированной нолем, мы прибавляем число, равное 2 в степени позиции в двоичном числе, отсчитанной с ноля, начиная справа. Поскольку позиция в строке и позиция в двоичном числе не совпадает, мы вычисляем ее через длину строки: l - i - 1. После выполнения цикла возвращаем результат res.
Вроде работает и хорошо. Но потом мне понадобилась обратная функция для перевода из десятичного числа в двоичное. Решение "в лоб" на этот раз мне показалось несколько громоздким и я задумался над оптимизацией алгоритма. Вспоминал институтский курс численных методов, но убедившись, что он был забыт мной начисто, нашел такой алгоритм в гугле
Code
string IntToBin(int i) { string s; if (i == 0) return("0"); while (i > 0) { s = StringConcatenate(i % 2, s); i = i / 2; } return(s); }
Здесь используется тот факт, что при умножении двоичного числа на 2 происходит сдвиг влево всех его битов и, соответственно, наоборот, при делении - сдвиг вправо. В остатке получаем значение текущего (крайнего правого) бита. В самом начале мы делаем проверку на 0. В случае если входное число равно нолю, то функция вернет пустую строку, вместо этого мы возвращаем строку "0" и прерываем выполнение функции. Далее в цикле while мы выполняем следующие действия: к результирющей сроке s добавляем слева бит, полученный в остатке от целочисленного деления на 2, и входное число i делим на 2. Деление целочисленное, поскольку тип переменной целочисленный. Цикл продолжается до тех пор, пока в результате деления мы не получим 0.
Алгоритм довольно изящный, но мало того, он еще и более быстродействующий, нежели решение "в лоб". Я задумался, что, наверное, и первую функцию неплохо было бы переписать с использованием умножения на 2. И мучила мысль, а будет ли существенный выигрыш? Стоит ли? Учитывая то, что данные функции мне потребовались для скрипта, в котором будет производиться работа с достатчоно большими массивами данных, а сами функции будут вызываться в огромных циклах, время выполнения может играть ощутимую роль.
Прежде всего я реализовал этот "изящный" алгоритм для перевода из десятичного числа в двоичное:
Code
int BinToInt2(string s) { int i; int res = 0; int l = StringLen(s); for (i = 0; i < l; i++) { res *= 2; if (StringGetChar(s, i) == '1') res += 1; } return(res); }
Суть его, я думаю, понятна. Разница только в цикле, где я сразу умножаю результирующую переменную на 2, для осуществления сдвига, а потом извлекаю очередной бит из строки. Если этот бит равен единице, то прибавляем эту единицу к результату.
Как же убедиться в том, каким будет выигрыш во времени и будет ли он вообще? Для этих целей обычно используется профилирование, но в MQL4 нет никаких встроенных средств для осуществления этой процедуры. Я решил воспользоваться старым проверенным методом больших циклов. Для осуществления профилирования функций я написал следующий "стендовый" скрипт:
extern int CycleCount = 10000000; //+------------------------------------------------------------------+ //| script program start function | //+------------------------------------------------------------------+ int start() { datetime before, after; int full; int i; //----
// source data string v = "11001010011100";
// get time before before = TimeLocal();
// profiling for (i = 0; i < CycleCount; i++) { // call function here BinToInt(v); }
// get time after after = TimeLocal(); full = after - before;
// out results Comment(StringConcatenate("Time before: ", before, " Time after: ", after, " Full time: ", full)); //---- return(0); }
Здесь в цикле из 10 млн итераций происходят многократные вызовы указанной функции. Время в секундах до и после выполнения цикла засекается. После чего делаются подсчет времени и вывод результата. Количество итераций я вынес в параметр скрипта, чтобы можно было подобрать подходящее. Рекомендуется задавать сразу небольшое количество, а потом аккуратно постепенно увеличивать его, иначе, переборщив, можно "подвесить" весь терминал.
В итоге получил следующие результаты. При использовании исходного решения в лоб: 13 секунд, а при использовании "изящного" алгоритма: 10 секунд.
Вывод. При достаточно больших колиествах вызовов разница в принципе минимальна, хотя иногда могут быть более ресурсоемкие задачи, для которых оптимизация по скорости выполнения будет иметь очень важное значение.
Будем благодарны, если Вы поделитесь этим материалом в социальных сетях:
Рейтинг дилинговых центров и брокеров Forex /
Форекс. О рынке Forex. Словарь трейдера. Инструменты Forex.
Видеоуроки Forex. Аналитика
www.forex-rating.at.ua