LINUX.ORG.RU

Рассчёт массива чисел Фибоначчи в compile-time C++

 , ,


1

4

На примере задачи о числах Фибоначчи хочу освоить программирование на плюсовых шаблонах. Задача - найти n-ное число Фибоначчи. Когда n известно, никаких проблем нет, решение понятно:

template<int n>
class Fibonacci {
public:
    static const int value = Fibonacci<n-1>::value + Fibonacci<n-2>::value;
};

template<>
class Fibonacci {
public:
    static const int value = 1;
};

template<>
class Fibonacci {
public:
    static const int value = 1;
};

Но n в момент компиляции неизвестно, известно лишь, что оно не больше 45. Поэтому я хотел бы насчитать все числа до 45-го и забить в массив, а потом выдать n-ное. Как это реализовать на шаблонах или constexpr?


Ответ на: комментарий от RazrFalcon

Я там нашёл только как нагенерить массив значений некоторой функции f в точках от 1 до N да построение массива по известному набору значений. Как строить массив из реккурентного соотношения - я не понимаю.

Было ещё решение с constexpr, которое почти подошло, но оно не работает на GCC < 5, а 5-ый крашит

SeTSeR
() автор топика

Вопрос интересный, хотя и из разряда lmgfy.

Вот товарищь показывает как произвольную метафункцию запрячь на фичах 0x. Выглядит правдоподобно.

Интуиция подсказывает, что можно и без variadic это сделать, но перечитывать Вандервуда на ночь глядя не охота :)

pon4ik ★★★★★
()
Ответ на: комментарий от SeTSeR

Было ещё решение с constexpr, которое почти подошло, но оно не работает на GCC < 5, а 5-ый крашит

Покажи его.

Я тут наклал перед сном:

template<size_t... indexes> struct fib_array {

  template<size_t n> constexpr static uint64_t gen_fib() {
    uint64_t a = 0, b = 1, i = n, x = 0;
    while(i--) x = a + b, a = b, b = x;
    return a;
  }

  constexpr static uint64_t data[] = {gen_fib<indexes>()...};
};

template<size_t ... indexes> constexpr uint64_t fib_array<indexes...>::data[];



template<template<size_t...> class F, size_t it, size_t end, bool = (it == end), size_t ... indexes> struct __gen {};

template<template<size_t...> class F, size_t it, size_t end, size_t ... indexes> struct __gen<F, it, end, false, indexes...> : __gen<F, it + 1, end, (it == end), indexes..., it> {};

template<template<size_t...> class F, size_t it, size_t end, size_t ... indexes> struct __gen<F, it, end, true, indexes...> : F<indexes...> {};

template<size_t n> using gen_fib = __gen<fib_array, 0, n>;
template<size_t begin, size_t end> using gen_fibbe = __gen<fib_array, begin, end>;


int main() {
  for(auto & x : gen_fib<10>::data) std::cout << x << " "; std::cout << std::endl;
  for(auto & x : gen_fibbe<20, 30>::data) std::cout << x << " "; std::cout << std::endl;
  for(auto & x : gen_fibbe<0, 253>::data) std::cout << x << " "; std::cout << std::endl;
}

Вроде работает.

AnonCxx
()
Ответ на: комментарий от AnonCxx
include <iostream>

template<int n>
struct Fibonacci {
        constexpr Fibonacci() : arr() {
                for(auto i = 0; i != (n-1); ++i)
                        arr[i] = Fibonacci<n-1>().arr[i];
                arr[n-1] = arr[n-2] + arr[n-3];
        }
        int arr[n];
};

template<>
struct Fibonacci<2> {
        int arr[2] = {1, 1};
};

template<>
struct Fibonacci<1> {
        int arr[1] = {1};
};

int main() {
        constexpr auto f = Fibonacci<45>();
        int n;
        std::cin >> n;
        std::cout << f.arr[n-1] << std::endl;
        return 0;
}

Проверял на ideone

SeTSeR
() автор топика

Стыбрил со stackowerflow вот такое решение:

#include <iostream>

template <int64_t... args>
struct ArrayHolder {
	static const int64_t data[sizeof...(args)];
};

template <int64_t... args>
const int64_t ArrayHolder<args...>::data[sizeof...(args)] = { args... };

template <size_t N, template<size_t> class F, int64_t... args>
struct generate_array_impl {
	typedef typename generate_array_impl<N-1, F, F<N>::value, args...>::result result;
};

template <template<size_t> class F, int64_t... args>
struct generate_array_impl<0, F, args...> {
	typedef ArrayHolder<F<0>::value, args...> result;
};

template <size_t N, template<size_t> class F>
struct generate_array {
	typedef typename generate_array_impl<N-1, F>::result result;
};

template <size_t N>
class Fibonacci {
	public:
		static const int64_t value = Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};

template <>
class Fibonacci<1> {
	public:
		static const int64_t value = 1;
};

template <>
class Fibonacci<0> {
	public:
		static const int64_t value = 0;
};

int main() {
	typedef generate_array<46, Fibonacci>::result Fibs;
	int n;
	std::cin >> n;
	std::cout << Fibs::data[n] << std::endl;
	return 0;
}
А вот насколько оно эффективно, я не знаю

SeTSeR
() автор топика

Ещё такой вопрос: как можно посмотреть, во что превратится код после раскрытия шаблонов?

SeTSeR
() автор топика

Шутили, что лисп — язык для вычисления факториалов, а вот теперь c++ — язык для вычисления чисел фибоначи, да ещё и в 100 строк кода

just-one-question
()
Ответ на: комментарий от just-one-question

Я про хаскель такое слышал

SeTSeR
() автор топика
Ответ на: комментарий от AIv

Гуляй лесом, мальчик. Речь про compile-time.

anonymous
()
Ответ на: комментарий от just-one-question

Только числа Фибоначчи лучше вычислять с помощью аккумулятора, и чтобы хвостовая рекурсия была. Ну, это так, небольшое замечание)

dave ★★★★★
()
Ответ на: комментарий от AIv

Я могу ещё короче:

fib =: 1:`([: +/ [: $:"0 -&1 , -&2)@.(>&1)
fib i. 10

Только где у тебя вычисление в компилетиме? Ну или просто замена выражения на результат ридером, как у меня?

just-one-question
()
Ответ на: комментарий от just-one-question
reduce(lambda L, x: L+[L[-2]+L[-1]], range(45), [1, 1])
fib =: 1:`([: +/ [: $:"0 -&1 , -&2)@.(>&1)

Ох. Как вы это читаете?

где у тебя вычисление в компилетиме?

А вот интересно, если вычислять перед компиляцией, можно это считать вычислением в compile-time?

Deleted
()
Ответ на: комментарий от just-one-question

На этом преимущества лишпа и заканчиваются.

Вопрос: что вы выберите:

  1. Не уметь вычислять факториал в 5 строк, но вместо этого всё остальное?
  2. Уметь вычислять факториал в 5 строк, но лишиться всего остального?

Промышленность выбрала второе.

anonymous
()
Ответ на: комментарий от AIv

да-да, и еще изменения в правилах сборки и билд-тайм зависимостях не забудьте пожалуйста :)

anonymous
()

Сколько часов оно так будет вычисляться? Я бы посчитал нормальным алгоритмом и захардкодил.

anonymous
()
Ответ на: комментарий от anonymous

Ну если Вы так просите;-)

tmp$ make -f fib.mk
g++ -o fib -Wall -g fib.cpp
tmp$ cat fib.mk
fib: fib.cpp fib.arr
        g++ -o fib -Wall -g fib.cpp

fib.arr:
        python -c "print str(reduce(lambda L, x: L+[L[-2]+L[-1]], range(45), [1, 1]))[1:-1]" > $@
tmp$ cat fib.cpp
#include <iostream>

long fib[] = {
#include "fib.arr"
};

int main(){
        for(int i=0; i<45; i++) std::cout<<fib[i]<<std::endl;
}

Конечно тут помногословней лиспа получилось, да.

AIv ★★★★★
()
Последнее исправление: AIv (всего исправлений: 1)
Ответ на: комментарий от Deleted

Ох. Как вы это читаете?

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

just-one-question
()
Ответ на: комментарий от anonymous

Вопрос: что вы выберите:

Не уметь вычислять факториал в 5 строк и вместе с этим ничего остального?

Уметь вычислять факториал в 5 строк и всё остальное?

Промышленность выбрала Java.

fixed

just-one-question
()
Ответ на: комментарий от just-one-question

В разных примерах кода на Си++ выше - по-разному, но везде ужасно как по форме, так и по содержанию ;)

dave ★★★★★
()
Ответ на: комментарий от just-one-question

Уметь вычислять факториал в 5 строк и всё остальное?

Всё остальное это что? Ничего?

Промышленность выбрала Java.

Жабу выбрала квалификация и низкие требования к конечному результату. И что за такая промышленность на жабке - я не знаю. В мире пони - возможно.

AnonCxx
()
Ответ на: комментарий от AnonCxx

Твоё решение? Ну там просто была ошибка в шаблонах, после её фикса всё заработало

SeTSeR
() автор топика
Ответ на: комментарий от AnonCxx

Всё остальное это что? Ничего?

Да. Lisp это факториалы и больше ничего. Как я и написал тут.

anonymous
()
Ответ на: комментарий от just-one-question

Э... довольно таки сложно сейчас найти систему без питона. Ну и чем то же надо проект собирать, если не устраивает gnumake - любой другой маке с этим думаю тоже справится без труда.

Насчет смысла - перечитайте название темы, и подумайте каким образом к нему относится Ваше решение? С каких пор это лисп стал С++?

Кстати лисп тащить в проект куда сложнее чем питон - что то я не помню, что бы он дефолтом где то ставился...

AIv ★★★★★
()
Ответ на: комментарий от AIv

перечитайте название темы, и подумайте каким образом к нему относится Ваше решение?

Какая задача, такое и решение. С++ только и может что понтоваться «модными» наворотами, устаревшими ещё лет 20 назад (когда там ANSI CL появился? В 94?). Язык пустых понтов и многословных конструкций — вот ваш c++

just-one-question
()
Ответ на: комментарий от AIv

перечитайте название темы, и подумайте каким образом к нему относится Ваше решение?

Лисп нужен для того, чтобы на каждом углу орать о том, что его используешь (хотя на самом деле не используешь)

anonymous
()
Ответ на: комментарий от just-one-question

С++ только и может что понтоваться «модными» наворотами, устаревшими ещё лет 20 назад

Приведите список наворотов и причину, по которым они устарели.

anonymous
()
Ответ на: комментарий от just-one-question

Какая задача, такое и решение.

Так зачем Вы вообще решаете такие задачи как эта?

Язык пустых понтов и многословных конструкций — вот ваш c++

Ваше мнение очень важно для всех нас, продолжайте и дальше его озвучивать. Не очень понятно правда зачем Вы вообще что то пишете в теме про этот противный c++.

AIv ★★★★★
()
Ответ на: комментарий от anonymous

Да ладно, некоторые товарищи весьма по делу кодят на лиспе плиски и еще много чего серьезного. Правда круг их весьма узок, и судя по упоротости just-one-question к ним не относится.

AIv ★★★★★
()
Ответ на: комментарий от AnonCxx

А насколько сложнее будет выглядеть решение с использованием уже насчитанных значений чисел Фибоначчи при подсчёте?

SeTSeR
() автор топика
Ответ на: комментарий от AIv

Результат втыкается в плюсовый файл при сборке

Вот эти бы слова да тем, кому не хватает шаблонов в go. Достаточно кодогенератора. И вот даже в плюсах ведь можно обойтись без ужасного шаблонного синтаксиса и бреда программирования времени компиляции.

gag ★★★★★
()
Ответ на: комментарий от SeTSeR

А зачем тебе это? Тормозит шаблонная лапша, а не constexpr.

template<size_t... seq> struct static_array {
  constexpr static uint64_t data[] = {seq...};
};
template<size_t ... seq> constexpr uint64_t static_array<seq...>::data[];


template<uint64_t n, uint64_t a = 0, uint64_t b = 1, uint64_t ... seq> struct __gen : __gen<n - 1, b, a + b, seq..., a> {};
template<uint64_t a, uint64_t b, uint64_t ... seq> struct __gen<0, a, b, seq...> : static_array<seq...>{};
AnonCxx
()
Ответ на: комментарий от just-one-question

А у меня все - кде, броузеры и прочая гуйня. Остальное си, но про си ты говорил то же самое.

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

По поводу твоих строчек - я написал вон фибоначи двумя строчками. Где сто строк кода - мне неведомо.

Естественно синтаксис всяких куллязычков короче, ибо синтаксиса нет. Типов нет, статики нет, нихрена нет. Фичей нет. В данном случае 50% длинны строк занимают имена типов.

Есть объективный факт - лисп мёртв. И ни один его апологет так и не смог написать на нём ничего, кроме лабы. Его удел - это скриптуха уровня а + б и лабы. Но и в скриптухе он не нужен, ибо есть более вменяемые языки с нормальным синтаксисом.

AnonCxx
()
Ответ на: комментарий от AnonCxx

кде

Ололо! Ну просто типичный образчик! Приходит вечерком с завода домой, залогинивается в KDE и под сиську пива компиляет генточку, попутно орошая ЛОР своими влажными фантазиями о том, что надо бны все переписать, чтобы было быстро-пребыстро.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.