Как эффективно превратить целые числа в Кодирование Фибоначчи?

последовательность Фибоначчи получается, начиная с 0 и 1, а затем добавляя два последних числа, чтобы получить следующий.

enter image description here

все положительные целые числа могут быть представлены как сумма множества чисел Фибоначчи без повторения. Например: 13 может быть суммой множеств {13}, {5,8} или {2,3,8}. Но, как мы видели, некоторые числа имеют более одного набора, сумма которых является числом. Если мы добавим ограничение, что множества не могут иметь два последовательные числа Фибоначчи, чем мы имеем уникальное представление для каждого числа.

для этого мы будем использовать двоичную последовательность (только нули и единицы). Например, 17 = 1 + 3 + 13. Затем, 17 = 100101. На рис. 2 подробное объяснение.

enter image description here

Я хочу превратить некоторые целые числа в это представление, но целые числа могут быть очень большими. Как это сделать эффективно.

5 ответов


сначала я хочу сказать вам, что мне очень понравился этот вопрос, я не знал, что все положительные целые числа могут быть представлены как сумма набора чисел Фибоначчи без повторения, я видел доказательство индукцией, и это было потрясающе.
Чтобы ответить на ваш вопрос, я думаю, что мы должны понять, как создается презентация. Я думаю, что простой способ найти это заключается в том, что из числа мы нашли ближайший минорный элемент Фибоначчи.
Например, если мы хотим представить 40:
У нас есть Fib (9)=34 и Fib(10)=55, поэтому первый элемент в презентации-Fib(9)
поскольку 40-Fib (9) = 6 и(Fib(5) =5 и Fib(6) =8) следующий элемент-Fib (5).
Итак, у нас есть 40 = Fib (9)+ Fib(5) + Fib(2)
Позвольте мне написать это на C#

 class Program
    {
        static void Main(string[] args)
        {
            List<int> fibPresentation = new List<int>();
            int numberToPresent = Convert.ToInt32(Console.ReadLine());
            while (numberToPresent > 0)
            {
                int k =1;
                while (CalculateFib(k) <= numberToPresent)
                {
                    k++;
                }
                numberToPresent = numberToPresent - CalculateFib(k-1);
                fibPresentation.Add(k-1);
            }
        }
        static int CalculateFib(int n)
        {
            if (n == 1)
                return 1;

            int a = 0;
            int b = 1;
            // In N steps compute Fibonacci sequence iteratively.
            for (int i = 0; i < n; i++)
            {
                int temp = a;
                a = b;
                b = temp + b;
            }
            return a;
        }
    }

ваш результат будет в fibPresentation


сама проблема простой. Вы всегда выбираете наибольшее число Фибоначчи меньше, чем остальное. Вы можете игнорировать ограничение с последовательными номерами (так как, если вам нужны оба, следующий-сумма обоих, поэтому вы должны были выбрать этот один вместо начальных двух).

таким образом, остается проблема, как быстро найти наибольшее число Фибоначчи меньше некоторого числа X. Есть известный трюк, который начинается с матрицы (назовите его М)

1 1
1 0

можно вычислить fibbonacci число перемножение матриц(х число М^Х). Более подробная информация здесь: https://www.nayuki.io/page/fast-fibonacci-algorithms . Конечный результат заключается в том, что вы можете вычислить число, которое вы смотрите в умножениях матрицы O(logN).

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

в целом это должно быть O (((logN)^2 * large_number_multiplications/additions)).


эта кодировка более точно называется "представлением Цекендорфа": см. https://en.wikipedia.org/wiki/Fibonacci_coding

работает жадный подход (см. https://en.wikipedia.org/wiki/Zeckendorf%27s_theorem) и вот некоторый код Python, который преобразует число в это представление. Он использует первые 100 чисел Фибоначчи и работает правильно для всех входов до 927372692193078999175 (и неправильно для любых больших входной.)

fibs = [0, 1]
for _ in xrange(100):
    fibs.append(fibs[-2] + fibs[-1])

def zeck(n):
    i = len(fibs) - 1
    r = 0
    while n:
        if fibs[i] <= n:
            r |= 1 << (i - 2)
            n -= fibs[i]
        i -= 1
    return r

print bin(zeck(17))

выход:

0b100101

поскольку жадный подход, похоже, работает, достаточно иметь возможность инвертировать отношение N=Fn.

по формуле Бине, Fn=[φ^n/√5], где скобки обозначают ближайшее целое число. Тогда с n=floor(lnφ(√5N)) вы очень близки к решению.

17 => n = floor(7.5599...) => F7 = 13
4 => n = floor(4.5531) => F4 = 3
1 => n = floor(1.6722) => F1 = 1

(Я не исключаю, что некоторые n значения могут быть от одного.)


Я не уверен, что это достаточно эффективно для вас, но вы можете просто использовать Backtracking, чтобы найти(действительное) представление.

Я бы попытался начать шаги возврата, взяв наибольшее возможное число fib и переключиться только на меньшие, Если подряд или только один раз ограничение нарушается.