Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > IT форум > Общие вопросы по программированию, компьютерный форум
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 11.12.2012, 09:15   #1
Kostia
Участник клуба
 
Аватар для Kostia
 
Регистрация: 21.11.2007
Сообщений: 1,690
По умолчанию Рекурсия VS Циклы

Интересно стало, вот и начал эксперимент. Задача вычисления быстрого вычисления числа в натуральной степени. Вот пара решений на C++:

Код:
inline __uint64_t myPowHlp(const __uint64_t &A, const __uint64_t &n, const __uint64_t &res)
{
    if(n == 0) return res; else
    if(n & 1)  return myPowHlp(A*A, n >> 1, res * A);
    else       return myPowHlp(A, n - 1, res * A);
}

inline __uint64_t myPow(const __uint64_t &A, const __uint64_t &n)
{
    return myPowHlp(A,n,1);
}
VS
Код:
inline __uint64_t fastPow(__uint64_t base, __uint64_t exponent)
{
    __uint64_t res = 1;
    while(exponent > 0)
    {
        if (exponent & 1)
        {
            exponent >>= 1;
            res *= base;
            base *= base;
        }
        else
        {
            exponent--;
            res *= base;
        }
    }
    return res;
}
тест:
Код:
    gettimeofday(&tstart, NULL);
    for(int i = 0; i < 10000000; i++) myPow(2, rand()%63); //fastPow
    gettimeofday(&tstop, NULL);
    seconds  = tstop.tv_sec  - tstart.tv_sec;
    useconds = tstop.tv_usec - tstart.tv_usec;
    std::cout << ((seconds) * 1000 + useconds/1000.0) + 0.5;
Рекурсия ~436.052 мСек
Цикл ~327.317 мСек

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

И еще, есть ли еще способы, не прибегая к асму, оптимизировать оба метода?
Kostia вне форума Ответить с цитированием
Старый 11.12.2012, 09:23   #2
pu4koff
Старожил
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,069
По умолчанию

Это не хвостовая рекурсия, т.к. два разных рекурсивных вызова может быть.
pu4koff вне форума Ответить с цитированием
Старый 11.12.2012, 09:36   #3
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,331
По умолчанию

Продлите експеримент дальше - сделайте стек у вашего приложения 4КБ и дерзайте с рекурсией....
waleri вне форума Ответить с цитированием
Старый 11.12.2012, 09:42   #4
Kostia
Участник клуба
 
Аватар для Kostia
 
Регистрация: 21.11.2007
Сообщений: 1,690
По умолчанию

Цитата:
Это не хвостовая рекурсия, т.к. два разных рекурсивных вызова может быть.
Ok. Поменял так:

Цитата:
inline __uint64_t myPowHlp(const __uint64_t &A, const __uint64_t &n, const __uint64_t &res)
{
if(n == 0) return res;// else
//if(n & 1) return myPowHlp(A*A, n >> 1, res * A);
else return myPowHlp(A, n - 1, res * A);
}
~95.736 мСек
WTF o_O.

Теперь появилась потребность выразить быстрое возведение в натуральную степень с хвостовой рекурсией. Хм, и как? Есть идеи или это невозможно?

Цитата:
Продлите експеримент дальше - сделайте стек у вашего приложения 4КБ и дерзайте с рекурсией....
Просто их нужно правильно готовить

Последний раз редактировалось Kostia; 11.12.2012 в 09:53.
Kostia вне форума Ответить с цитированием
Старый 11.12.2012, 09:43   #5
Abstraction
Старожил
 
Аватар для Abstraction
 
Регистрация: 25.10.2011
Сообщений: 3,178
По умолчанию

Цитата:
И еще, есть ли еще способы, не прибегая к асму, оптимизировать оба метода?
Не очень понятно, почему Вы так разделяете действия при чётном и нечётном exp:
Код:
inline __uint64_t fastPow(__uint64_t base, __uint32_t exponent)//64 бита на показатель степени? Гм...
{
    __uint64_t res = 1;
    while(exponent != 0)
    {
        if (exponent & 1) res *= base;
        base *= base;
        exponent >>= 1;
    }
    return res;
}
То же в первом примере:
Код:
inline __uint64_t myPowHlp(const __uint64_t A, const __uint32_t n, const __uint64_t acc)
{
    if(n == 0) return acc;
    if(n & 1)  return myPowHlp(A*A, n >> 1, acc * A);
    else       return myPowHlp(A*A, n >> 1, acc);
}
P.S. Вообще-то, первый пример можно ещё скорректировать:
Код:
__uint64_t myPowHlp(const __uint64_t A, const __uint32_t n, const __uint64_t acc)
{
    if(n == 0) return acc;
    return myPowHlp(A*A, n >> 1, (n&1) ? acc*A : acc);
}

Последний раз редактировалось Abstraction; 11.12.2012 в 09:59.
Abstraction вне форума Ответить с цитированием
Старый 11.12.2012, 09:45   #6
Abstraction
Старожил
 
Аватар для Abstraction
 
Регистрация: 25.10.2011
Сообщений: 3,178
По умолчанию

Цитата:
Это не хвостовая рекурсия, т.к. два разных рекурсивных вызова может быть.
Это почему? Хвостовая рекурсия - когда возвращается строго результат вызова самой функции с другими аргументами. Независимо от того, сколько есть разных логических путей, каждый можно заменить на изменение значений аргументов и goto в начало.
Abstraction вне форума Ответить с цитированием
Старый 11.12.2012, 10:23   #7
Kostia
Участник клуба
 
Аватар для Kostia
 
Регистрация: 21.11.2007
Сообщений: 1,690
По умолчанию

Цитата:
(n&1) ? acc*A : acc
Чем не еще один if?
Цитата:
//64 бита на показатель степени? Гм...
//32 бита на степень?
вообще раньше был __uint8_t, но потом все превратилось в 64 бита

Ok. Ваши варианты уравняли производительность.
Еще для меня стало непонятным то, что если в рекурсии передавать значения по ссылке, то скорость резко падает, почему?

Как-то так на haskell перевел:
Код:
fastPow :: Int -> Int -> Int
fastPow a b = fastPow' a b 1
fastPow' a b acc | b == 0 = acc
                 | (.&.) b 1 == 1 = fastPow' (a*a) (shiftR b 1) (acc * a)
                 | otherwise = fastPow' (a*a) (shiftR b 1) acc

main =do
    start <- (getClockTime >>= toCalendarTime)
    print (fastPow 2 62)
    stop <- (getClockTime >>= toCalendarTime)
    print (makeTime stop - makeTime start)
еще такой вариант:

Код:
fastPow :: Int -> Int -> Int
fastPow a b = fastPow' a b 1
fastPow' a b acc | b == 0 = acc
                 | otherwise = fastPow' (a*a) (shiftR b 1) (r a b acc)
                     where 
                        r a b acc | (.&.) b 1 == 1 = acc * a
                                  | otherwise = acc
Цитата:
~95.736 мСек
WTF o_O.
Не правильно, значение функции просто напросто не считалось, т.к. было не нужно, вычислялся только rand().

Последний раз редактировалось Kostia; 11.12.2012 в 11:08.
Kostia вне форума Ответить с цитированием
Старый 11.12.2012, 11:08   #8
pu4koff
Старожил
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,069
По умолчанию

Цитата:
Сообщение от Abstraction Посмотреть сообщение
Это почему? Хвостовая рекурсия - когда возвращается строго результат вызова самой функции с другими аргументами. Независимо от того, сколько есть разных логических путей, каждый можно заменить на изменение значений аргументов и goto в начало.
Хотя да. Пожалуй всё же там хвостовая рекурсия. Просто такую конструкцию проблематично оптимизатору развернуть, т.к. там условный вызов идёт, а условия имеют привычку предсказываться и выполняться наперёд.
pu4koff вне форума Ответить с цитированием
Старый 11.12.2012, 11:23   #9
Kostia
Участник клуба
 
Аватар для Kostia
 
Регистрация: 21.11.2007
Сообщений: 1,690
По умолчанию

есть еще такой вариант и он работает быстрее, вполне возможно что это из-за лишнего вызова функции?

Код:
__uint64_t myPow2(const __uint64_t A, const __uint64_t n)
{
    if(n == 0) return 1;
    return myPow2(A*A, n >> 1) * ((n&1) ? A : 1);
}
Kostia вне форума Ответить с цитированием
Старый 11.12.2012, 11:31   #10
Abstraction
Старожил
 
Аватар для Abstraction
 
Регистрация: 25.10.2011
Сообщений: 3,178
По умолчанию

Цитата:
есть еще такой вариант и он работает быстрее, вполне возможно что это из-за лишнего вызова функции?
Полагаю, что из-за отсутствия ветвления в аргументах. Но это в нерекурсивный вызов компилятор не преобразует заведомо.
Цитата:
//32 бита на степень?
Машинное слово, почему нет.
Abstraction вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
синусы и ко. циклы, вроде циклы Scorch92 Паскаль, Turbo Pascal, PascalABC.NET 2 22.12.2010 19:26
Рекурсия <Tyz> Паскаль, Turbo Pascal, PascalABC.NET 3 18.12.2010 23:22
рекурсия DinaraIITU Помощь студентам 3 04.11.2010 15:39
Рекурсия на С++ Nitriyc Помощь студентам 0 28.04.2010 17:22
Циклы - вложенны циклы? tigga Microsoft Office Excel 5 19.02.2010 23:36