Факторизация Фибоначчи

21

Числа Фибоначчи

Числа Фибоначчи начинаются с f(1) = 1и f(2) = 1(некоторые входят , f(0) = 0но это не имеет никакого отношения к этой проблеме. Тогда для n > 2, f(n) = f(n-1) + f(n-2).

Соревнование

Ваша задача - найти и вывести n-е положительное число, которое может быть выражено как произведение чисел Фибоначчи. Вы можете выбрать 0 или 1, в зависимости от того, что вам больше подходит, но вы должны указать это в своем ответе.

Кроме того, ваш ответ должен вычислить сотый срок за разумное время.

Testcases

n   result corresponding product (for reference)
1   1      1
2   2      2
3   3      3
4   4      2*2
5   5      5
6   6      2*3
7   8      2*2*2 or 8
8   9      3*3
9   10     2*5
10  12     2*2*3
11  13     13
12  15     3*5
13  16     2*2*2*2 or 2*8
14  18     2*3*3
15  20     2*2*5
16  21     21
17  24     2*2*2*3 or 3*8
18  25     5*5
19  26     2*13
20  27     3*3*3
100 315    3*5*21

Ссылки

Дрянная Монахиня
источник
В тестовом примере почему некоторые из них n = результат, тогда как для 7 и выше они не равны. Может быть, я не понимаю вопроса. Но я просто хочу проверить
Джордж
1
7не может быть выражено как произведение чисел Фибоначчи. Следовательно, 1первое требуемое число - это 1, 2есть 2, ..., 6есть 6, но 7есть 8.
Утренняя монахиня
Ах, конечно, это имеет смысл
Джордж
Должны ли вы распечатать все способы изготовления номера. Например, 16 имеет два способа, или вы можете просто вывести один?
Георгий
3
@george Я верю, что " corresponding product" только для разъяснения. Ваш код должен только вывести " result".
Трихоплакс

Ответы:

6

Желе , 26 24 23 21 байт

ÆDf÷߀FðḊ¡
1ç#2+С1¤Ṫ

Попробуйте онлайн!

Как это устроено

1ç#2+С1¤Ṫ  Main link. Argument: n (integer)

        ¤   Combine the three links to the left into a niladic chain.
   2          Set the left argument and the return value to 2 (third positive
              Fibonacci number).
       1      Yield 1 (second positive Fibonacci number).
    +С       Compute the sum of the return value and right argument, replacing the
              return value with the sum and the right argument with the previous
              return value.
              Do this n times, collecting all return values in a list.
              This returns A, the first n Fibonacci numbers greater than 1.
1             Set the return value to 1.
 ç#           Call the helper link with left argument k = 1, 2, 3... and right
              argument A = [2, 3, 5...] until n of them return a truthy value.
              Collect the matches in a list.
           Ṫ  Tail; extract the last (n-th) match.


ÆDf÷߀FðḊ¡    Helper link. Left argument: k. Right argument: A

        Ḋ     Dequeue; yield r := [2, ..., k].
       ð ¡    If r in non-empty, execute the chain to the left. Return k otherwise.
ÆD              Yield the positive divisors of k.
   ÷            Divide k by all Fibonacci numbers in A.
  f             Filter; keep divisors that belong to k÷A, i.e., all divisors
                d for which k÷d belongs to A.
    ߀          Recursively call the helper link for each kept divisor d, with left
                argument d and right argument A.
      F         Flatten the result, yielding a non-empty array iff any of the
                recursive calls yielded a non-empty array or a number.
                If the left argument is 1, the helper link returns 1, so the
                array will be non-empty if the consecutive divisions by Fibonacci
                numbers eventually produced a 1.
Деннис
источник
2
Какова сложность этого алгоритма с точки зрения ввода?
Утренняя монахиня
В любом случае, это очень быстро! Менее чем за 2 секунды на сотый срок
Луис Мендо
@ LeakyNun Я понятия не имею, как рассчитать это, но если посмотреть, как ввод 400 занимает в 32 раза больше времени, чем ввод 100, я бы сказал, что это экспоненциально. Обращается с 100 с легкостью, хотя.
Деннис
1
Ну, только вы знаете, каков ваш алгоритм ...
Leaky Nun
Мне удалось сделать это намного быстрее, не пересчитывая последовательность Фибоначчи для каждого проверенного числа. Я добавлю объяснение, как только я закончу играть в гольф.
Деннис
5

Юлия, 79 байт

!k=any(i->√(5i^2+[4,-4])%1k%i<!(k÷i),2:k)^~-k
<|(n,k=1)=n>0?n-!k<|-~k:~-k

Попробуйте онлайн!

Задний план

В « Продвинутых задачах и решениях» H-187: Фибоначчи - это квадрат , предлагающий показывает, что

Личность Фибоначчи / Лукаса

где L n обозначает n- е число Лукаса , и это - наоборот - если

обратная личность Фибоначчи / Лукаса

тогда n - это число Фибоначчи, а m - это число Лукаса.

Как это устроено

Мы определяем бинарный оператор <|для наших целей. Он не определен в последних версиях Julia, но все еще распознается синтаксическим анализатором как оператор.

Когда вызывается только с одним аргументом ( n ), <|инициализирует k как 1 . Хотя n положительно, оно вычитает ! K ( 1, если k - произведение чисел Фибоначчи, 0, если нет) из n и рекурсивно вызывает себя, увеличивает k на 1 . Как только n достигает 0 , желаемое количество продуктов найдено, поэтому <|возвращается предыдущее значение k , то есть ~ -k = k - 1 .

Унарный оператор !, переопределенный как тест для числовых произведений Фибоначчи, выполняет свою задачу следующим образом.

  • Если k = 1 , k является произведением чисел Фибоначчи. В этом случае мы возвращаем возвращаемое значение any(...)до степени ~ -k = k - 1 = 0 , поэтому результат будет равен 1 .

  • Если k> 1 , результатом будет значение any(....), которое будет возвращать true тогда и только тогда, когда предикат √(5i^2+[4,-4])%1∋k%i<!(k÷i)возвращает true для некоторого целого числа i, такого что 2 ≤ i ≤ k .

    Цепные условия в предикате выполняются, если k%iпринадлежит √(5i^2+[4,-4])%1и k%iменьше, чем !(k÷i).

    • √(5i^2+[4,-4])%1берет квадратный корень из 5i 2 + 4 и 5i 2 - 4 и вычисляет их вычеты по модулю 1 . Каждый модуль равен 0, если соответствующее число является идеальным квадратом, и положительное число меньше 1 в противном случае.

      Поскольку k%iвозвращает целое число, оно может принадлежать массиву модулей только в том случае, если k% i = 0 (т. Е. K делится на i ) и хотя бы один из 5i 2 + 4 и 5i 2 - 4 является идеальным квадратом (т. Е. я - число Фибоначчи).

    • !(k÷i)рекурсивно вызывает 1 с аргументом k ÷ i (целочисленное деление), которое будет больше 0, если и только если k ÷ i является произведением чисел Фибоначчи.

По индукции ,! имеет желаемое свойство.

Деннис
источник
5

Python, 90 байт

f=lambda n,a=2,b=3:n<2or n%a<f(n/a)or n-a>0<f(n,b,a+b)
g=lambda k,n=1:k and-~g(k-f(n),n+1)

Основная функция gвыводит произведение kФибоначчи, 1-индексированное. Это вычисляется g(100)как 315почти мгновенно. Это происходит с общим рекурсивным рецептом подсчета чисел в nпоисках kэкземпляров, которые удовлетворяют функции f. Каждый такой экземпляр понижает необходимое количество, kпока не достигнет 0.

Вспомогательная функция fпроверяет число на наличие произведения Фибоначчи. Он рекурсивно генерирует числа Фибоначчи в своих необязательных аргументах aи b. Он выводит «да», если выполняется одно из следующих условий:

  • n<2, Это подразумевает n==1тривиальное произведение)
  • n%a<f(n/a), Это требует n%a==0и f(n/a)==True, т. Е. nЭто кратное число Фибоначчи a, и устранение этого фактора по- aпрежнему дает продукт Фибоначчи.
  • n-a>0<f(n,b,a+b), эквивалентно n>a and f(n,b,a+b). Проверяет, что текущее число Фибоначчи, которое тестируется, по меньшей мере n, не работает , и работает большее число Фибоначчи. Спасибо Деннису за 2 сохраняющих байта, использующих вместо неравенства короткое замыкание and.

Функция gможет быть на один байт короче

lambda k:filter(f,range(k*k+1))[k]

if g(k)всегда самое большее k*k, что я не уверен, асимптотически верно. Границы 2**kдостаточно, но тогда она g(100)занимает слишком много времени. Может быть, вместо этого gможно сделать рекурсивный из f.

XNOR
источник
Согласно этой таблице в OEIS, g(k)превышает, k*kкогда k = 47000и выше.
Исаак
2

Perl 6 ,  95  93 байта

{(1..*).grep({$/=$_;map {->{$/%$_||($//=$_);$/}...*!%%$_;0},reverse 2,3,&[+]...*>$_;2>$/})[$_]}
{(1..*).grep({$/=$_;map {->{$/%$_||($//=$_);$/}...*%$_;0},reverse 2,3,&[+]...*>$_;2>$/})[$_]}

(0 основанный индекс)

Тест:

my &fib-prod = {(1..*).grep({$/=$_;map {->{$/%$_||($//=$_);$/}...*%$_;0},reverse 2,3,&[+]...*>$_;2>$/})[$_]}

say fib-prod 0 ..^ 20;
# (1 2 3 4 5 6 8 9 10 12 13 15 16 18 20 21 24 25 26 27)
say time-this { say fib-prod 100 -1; };
# 315
# 1.05135779

sub time-this (&code) {
  my $start = now;
  code();
  now - $start;
}

Объяснение:

{
  (1..*).grep(
    {
      $/ = $_; # copy the input ($_) to $/
      map { # map used just for side effect
        ->{
          $/ % $_    # if $/ is divisible by the current fib factor
        ||
          ($/ /= $_) # divide it out once
        ;
          # return the current value in $/
          $/
        }
        ... # repeat until that returns:
        * !%% $_ # something that is not divisible by the current fib factor
        ;0
      },
      # the possible fibonacci factors plus one, reversed
      # ( the extra is to save one byte )
      reverse 2,3,&[+] ... *>$_;

      # is the end result of factoring equal to 1
      # ( for the grep above )
      2 > $/
    }
  )[ $_ ] # get the value at 0-based index
}
Брэд Гилберт b2gills
источник
2

Python 3, 175 170 148 байт

Спасибо @Dennis за -22 байта

j=x=int(input())
y=1,1
exec('y+=y[-2]+y[-1],;'*x)
i=c=0
while c<x:
    if j>=x:j=0;i+=1;t=i
    if t%y[~j]<1:t/=y[~j];j-=1
    if t<2:c+=1;j=x
    j+=1
print(i)

Принимает ввод из STDIN и печатает в STDOUT. Это одноиндексное. Вычисление 100-го члена занимает примерно одну десятую секунды.

Как это устроено

j=x=int(input())                Get term number x from STDIN and set Fibonacci number index
                                j to x to force initialisation of j later 
y=1,1                           Initialise tuple y with start values for Fibonacci sequence
exec('y+=y[-2]+y[-1],;'*x)      Compute the Fibonacci sequence to x terms and store in y
i=c=0                           Initialise test number i and term counter c
while c<x:                      Loop until x th term is calculated
    if j>=x:j=0;i+=1;t=i        Initialise Fibonacci number index j, increment i and
                                initialise temp variable t for looping through all j for
                                some i. Executes during the first pass of the loop since
                                at this point, j=x
    if t%y[~j]<1:t/=y[~j];j-=1  Find t mod the j th largest Fibonacci number in y and if no
                                remainder, update t by dividing by this number.
                                Decrementing j means that after a later increment, no
                                change to j occurs, allowing for numbers that are 
                                divisible by the same Fibonacci number more than once by
                                testing again with the same j
    if t<2:c+=1;j=x             If repeated division by ever-smaller Fibonacci numbers
                                leaves 1, i must be a Fibonacci product and c is
                                incremented. Setting j equal to x causes j to be reset
                                to 0 during the next loop execution
    j+=1                        Increment j
print(i)                        i must now be the x th Fibonacci product. Print i to STDOUT

Попробуйте это на Ideone

TheBikingViking
источник
2

Python 2, 120 107 байт

g=lambda k:1/k+any(k%i==0<g(k/i)for i in F)
F=2,3;k=0;n=input()
while n:F+=F[k]+F[-1],;k+=1;n-=g(k)
print k

Проверьте это на Ideone .

Как это устроено

Мы инициализируем F как кортеж (2, 3) (первые два числа Фибоначчи больше 1 ), k как 0 и n как целое число, считанное из STDIN.

Пока n положительно, мы делаем следующее:

  • Дозапись следующее число Фибоначчи, вычисляется как F [K] + Ж [-1] , то есть, сумма двух последних элементов F до кортежа F .

  • Инкремент к .

  • Вычтите g (k) из n .

g возвращает 1 тогда и только тогда, когда k является произведением чисел Фибоначчи, поэтому, когда n достигает 0 , k является n- м числом Фибоначчи, и мы печатаем его в STDOUT.

г достигает своей цели следующим образом.

  • Если k равно 1 , это произведение чисел Фибоначчи, и 1/kмы уверены, что мы возвращаем 1 .

  • Если к больше чем 1 , мы называем g(k/i)рекурсивно для всех чисел Фибоначчи I в F .

    g(k/i)рекурсивно проверяет, является ли k / i произведением числа Фибоначчи. Если g(k/i)возвращается 1 и i делит k равномерно, k% i = 0 и условие k%i<g(k/i)выполняется, поэтому g вернет 1 тогда и только тогда, когда существует число Фибоначчи, такое, что k является произведением этого числа Фибоначчи и другого произведения чисел Фибоначчи.

Деннис
источник
1

JavaScript (ES6), 136

Таким образом, довольно медленно играю в гольф, вычисляя термин 100 за 8 секунд на моем ПК.

(n,F=i=>i>1?F(i-1)+F(i-2):i+1,K=(n,i=1,d,x)=>eval('for(;(d=F(i++))<=n&&!(x=!(n%d)&&K(n/d)););x||n<2'))=>eval('for(a=0;n;)K(++a)&&--n;a')

Меньше гольфа и быстрее (избегая eval)

n=>{
  F=i=> i>1 ? F(i-1)+F(i-2) : i+1; // recursive calc Fibonacci number
  K=(n,i=1,d,x)=>{ // recursive check divisibility
    for(; (d=F(i++))<=n && !(x=!(n%d)&&K(n/d)); );
    return x||n<2
  };
  for(a=0; n; )
    K(++a) && --n;
  return a
}

Тестовое задание

X=(n,F=i=>i>1?F(i-1)+F(i-2):i+1,K=(n,i=1,d,x)=>eval('for(;(d=F(i++))<=n&&!(x=!(n%d)&&K(n/d)););x||n<2'))=>eval('for(a=0;n;)K(++a)&&--n;a')

function test() {
  var i=+I.value
  O.textContent=X(i)
}

test()
<input id=I value=100 >
<button onclick="test()">Go</button><pre id=O></pre>

edc65
источник
1

Haskell, 123 байта

f=2:scanl(+)3f
m((a:b):c)=a:m(b?(a#c))
v#((a:b):c)|v==a=b?(v#c)
_#l=l
y?(z:e)|y>z=z:y?e
a?b=a:b
l=1:m[[a*b|b<-l]|a<-f]
(l!!)

Очень ленивый, бесконечный!

Возможно, это не самый короткий путь, но мне пришлось попробовать этот подход, обобщение довольно известного метода для вычисления списка чисел Хэмминга. fсписок чисел Фибоначчи, начинающийся с 2. Для краткости скажем, что lol (список списков) - это бесконечный список упорядоченных бесконечных списков, упорядоченных по их первым элементам. mэто функция для объединения LOL, удаления дубликатов. Он использует две вспомогательные функции инфикса. ?вставляет бесконечный отсортированный список в лол. #удаляет значение из lol, которое может появиться в качестве заголовка первых списков, повторно вставляя оставшийся список с помощью ?.

Наконец, lсписок чисел, являющихся произведением чисел Фибоначчи, определяется как 1, за которым следует объединение всех списков, полученных умножением lна число Фибоначчи. В последней строке указывается требуемая функция (как обычно, без привязки к имени, поэтому не копируйте ее как есть), которая используется !!для индексации в списке, что делает функцию индексированной 0.

Нет проблем с вычислением 100-го или 100-тысячного числа.

Кристиан Сиверс
источник
0

Python 2, 129 128 125 123 121 байт

g=lambda k:1/k|any(abs(round(5**.5*i)**2-5*i*i)==4>k%i<g(k/i)for i in range(k+1))
f=lambda n,k=1:n and f(n-g(k),k+1)or~-k

Проверьте это на Ideone .

Деннис
источник