En Haskell, comment puis-je générer des nombres de Fibonacci basés sur la propriété que le nième nombre de Fibonacci est égal au (n-2) ème nombre de Fibonacci plus le (n-1) ème nombre de Fibonacci?
J'ai vu ceci:
fibs :: [Integer]
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
Je ne comprends pas vraiment cela, ni comment cela produit une liste infinie au lieu d’une liste contenant 3 éléments.
Comment pourrais-je écrire du code haskell qui fonctionne en calculant la définition réelle et non en faisant quelque chose de vraiment bizarre avec les fonctions de liste?
Voici une fonction différente et plus simple qui calcule le nième nombre de Fibonacci:
fib :: Integer -> Integer
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
L’implémentation à laquelle vous faites référence repose sur des observations sur la manière dont les valeurs de Fibonacci sont liées (et sur la façon dont Haskell peut définir des structures de données en termes de soi même, créant ainsi des structures de données infinies).
La fonction dans votre question fonctionne comme ceci:
Supposons que vous ayez déjà une liste infinie de nombres de Fibonacci:
[ 1, 1, 2, 3, 5, 8, 13, .... ]
La tail
de cette liste est
[ 1, 2, 3, 5, 8, 13, 21, .... ]
zipWith
combine deux listes élément par élément à l'aide de l'opérateur donné:
[ 1, 1, 2, 3, 5, 8, 13, .... ]
+ [ 1, 2, 3, 5, 8, 13, 21, .... ]
= [ 2, 3, 5, 8, 13, 21, 34, .... ]
Ainsi, la liste infinie de nombres de Fibonacci peut être calculée en ajoutant les éléments 1
et 1
au résultat de la compression de la liste infinie de nombres de Fibonacci avec la queue de la liste infinie de nombres de Fibonacci à l'aide de l'opérateur +
.
Maintenant, pour obtenir le nième numéro de Fibonacci, récupérez simplement le nième élément de la liste infinie de nombres de Fibonacci:
fib n = fibs !! n
La beauté de Haskell est qu’elle ne calcule aucun élément de la liste des nombres de Fibonacci avant d’avoir besoin de l’aide.
Est-ce que j'ai fait exploser ta tête? :)
selon la définition, chaque élément de la série fibonacci est la somme des deux termes précédents. mettre cette définition dans haskell paresseux vous donne ça!
fibo a b = a:fibo b (a+b)
maintenant, il suffit de prendre n éléments de fibo à partir de 0,1
take 10 (fibo 0 1)
Pour développer la réponse de dtb:
Il y a une différence importante entre la solution "simple":
fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
Et celui que vous avez spécifié:
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
La solution simple prend O (1.618NN) temps pour calculer le Nième élément, tandis que celui que vous avez spécifié prend O (N2). En effet, celui que vous avez spécifié prend en compte le fait que l’informatique fib n
et fib (n-1)
(qui est nécessaire pour le calculer) partagent la dépendance de fib (n-2)
et qu’elle peut être calculée une fois pour les deux pour gagner du temps. SUR2) est pour N additions de nombres de O(N) chiffres.
Il existe un certain nombre d'algorithmes de Haskell différents pour la séquence de Fibonacci ici . L'implémentation "naïve" ressemble à ce que vous recherchez.
Cette implémentation calcule le 100 000e nombre de Fibonacci presque instantanément:
fib = fastFib 1 1
fastFib _ _ 0 = 0
fastFib _ _ 1 = 1
fastFib _ _ 2 = 1
fastFib a b 3 = a + b
fastFib a b c = fastFib (a + b) a (c - 1)
Une manière paresseuse de générer une série infinie de séries de Fibonacci peut facilement être obtenue par unfoldr
comme suit;
fibs :: [Integer]
fibs = unfoldr (\(f,s) -> Just (f,(s,f+s))) (0,1)
LOL, j'adore l'appariement de motifs Haskell, mais il est rendu inutile dans les fonctions standard de Fibonacci. La liste standard est construite à partir de la droite. Pour utiliser la correspondance de modèle et les inconvénients, la liste doit être construite à partir de la gauche. Une consolation, au moins, c’est que c’est très rapide. ~ O (n), ça devrait être. Une fonction d'assistance est nécessaire pour inverser la liste infinie (ce que vous ne pouvez faire que dans Haskell, joie) et cette fonction génère chaque liste suivante de l'exécution, de sorte que "dernier" est également utilisé dans le pipeline de fonctions d'assistance.
f (x:y:xs) = (x+y):(x:(y:xs))
L'assistant
fib n = reverse . last . take n $ iterate f [1,0]
Ceci est une version de la liste et, je pense, explique la manière dont la liste est construite. Je veux faire une version Tuple.
Modifier le 15/03/2018
Tout d’abord, Will Ness m’a éclairé sur le fait qu’une liste complète générée à chaque itération était inutile et que seules les deux dernières valeurs utilisées étaient nécessaires et que les valeurs de la liste de résultats étaient les premières valeurs de chaque liste ou paire générée. C'était tellement drôle. Après que Will m'ait dit que les valeurs de la liste étaient les premières valeurs des listes, je l'ai exécutée et j'ai vu les valeurs 0,1,1,2,3,5,8,13 en tête de chaque liste, j'ai dit WTF, Will va-t-il changer mon code sur mon PC? Les valeurs étaient là mais comment !? Après un moment, j'ai réalisé qu'ils étaient toujours là mais je ne les ai pas vus. ugh . La version de Will de la fonction et de la fonction d'assistance est:
f = (\(x:y:xs) -> (x+y):x:xs) -- notice, no y: put back only x+y & x
et sa fonction d'assistance réécrire
fib n = map head . take n $iterate f [0,1]
Je pense aussi qu'ils peuvent maintenant être combinés:
fib n = take n . map head $ iterate (\(x:y:xs) -> (x+y):x:xs) [0,1]
En dehors de toute pertinence, la fonction peut être aussi avec des n-uplets
fib n = take n . map fst $ iterate (\(a,b) -> (b,a+b)) (0,1)
Un autre formulaire, un formulaire de compréhension de liste, peut également être écrit pour tous:
fib n = take n [ fst t | t <- iterate (\(a,b) -> (b,a+b)) (0,1)]
Ce sont tous itératifs et robustes. Le plus rapide est la carte avec des listes à 12,23 secondes pour fib 5000. La compréhension de Tuple était la deuxième plus rapide pour fib 5000 à 13,58 secondes.