J'essayais donc d'écrire le n ème nombre dans la séquence de Fibonacci dans une fonction aussi compacte que possible:
public uint fibn ( uint N )
{
return (N == 0 || N == 1) ? 1 : fibn(N-1) + fibn(N-2);
}
Mais je me demande si je peux rendre cela encore plus compact et efficace en changeant
(N == 0 || N == 1)
en une seule comparaison. Existe-t-il une opération de décalage de bits sophistiquée qui peut faire cela?
c#
algorithm
optimization
arithmetic-expressions
utilisateur6048670
la source
la source
fibn(N-1) + fibn(N-2)
au lieu deN * fibn(N-1)
?Réponses:
Celui-ci fonctionne également
la racine carrée de 0 et 1 renverra respectivement 0 et 1.
la source
Math.Sqrt
est une fonction à virgule flottante compliquée. Il fonctionne lentement par rapport aux alternatives uniquement entiers !!Il existe plusieurs façons d'implémenter votre test arithmétique en utilisant l'arithmétique au niveau du bit. Votre expression:
x == 0 || x == 1
est logiquement équivalent à chacun de ceux-ci:
(x & 1) == x
(x & ~1) == 0
(x | 1) == 1
(~x | 1) == (uint)-1
x >> 1 == 0
Prime:
x * x == x
(la preuve demande un peu d'effort)Mais en pratique, ces formulaires sont les plus lisibles, et la petite différence de performances ne vaut pas vraiment la peine d'utiliser l'arithmétique au niveau du bit:
x == 0 || x == 1
x <= 1
(carx
est un entier non signé)x < 2
(carx
est un entier non signé)la source
(x & ~1) == 0
x == 0 || x == 1
que pour(x & ~1) == 0
ou(x | 1) == 1
. Pour le premier, il est assez intelligent pour le reconnaître comme étant équivalent àx <= 1
un simplecmpl; setbe
. Les autres le confondent et le font générer un code pire.Puisque l'argument est
uint
( non signé ), vous pouvez mettreMoins lisible (IMHO) mais si vous comptez chaque caractère ( Code Golf ou similaire)
Modifier : pour votre question modifiée :
Ou
la source
return N<2?1:f(N-1)+f(n-2)
. : PVous pouvez également vérifier que tous les autres bits sont à 0 comme ceci:
Pour être complet grâce à Matt, la solution encore meilleure:
Dans les deux cas, vous devez faire attention aux parenthèses car les opérateurs au niveau du bit ont une priorité inférieure à
==
.la source
(N|1)==1
Si vous souhaitez rendre la fonction plus efficace, utilisez une table de recherche. La table de recherche est étonnamment petite avec seulement 47 entrées - l'entrée suivante déborderait d'un entier non signé de 32 bits. Cela rend également la fonction facile à écrire.
Vous pouvez évidemment faire la même chose pour les factorielles.
la source
Comment le faire avec bitshift
Si vous voulez utiliser bitshift et rendre le code quelque peu obscur (mais court), vous pouvez faire:
Pour un entier non signé
N
dans la langue c,N>>1
jette le bit de poids faible. Si ce résultat est non nul, cela implique que N est supérieur à 1.Remarque: cet algorithme est horriblement inefficace car il recalcule inutilement les valeurs de la séquence qui ont déjà été calculées.
Quelque chose de beaucoup plus rapide
Calculez-le en une seule passe plutôt que de construire implicitement un arbre de taille fibonaci (N):
Comme certains l'ont mentionné, il ne faut pas longtemps pour déborder même un entier non signé de 64 bits. En fonction de la taille que vous essayez d'atteindre, vous devrez utiliser des entiers de précision arbitraires.
la source
uint
n'est pas implicitement convertible enbool
, et la question est spécifiquement marquée comme C #.--N != 0
place. Le fait est que quelque chose de O (n) est préférable à O (fibn (n)).Lorsque vous utilisez un uint, qui ne peut pas être négatif, vous pouvez vérifier si
n < 2
ÉDITER
Ou pour ce cas de fonction spécial, vous pouvez l'écrire comme suit:
ce qui conduira au même résultat, bien entendu au prix d'une étape de récursion supplémentaire.
la source
1 * fibn(0) = 1 * 1 = 1
fibn
alorsVérifiez simplement si
N
est <= 1 puisque vous savez que N est non signé, il ne peut y avoir que 2 conditionsN <= 1
qui aboutissent àTRUE
: 0 et 1la source
(N == 0 || N == 1)
. Vous savez qu'il ne sera pas inférieur à 0 (car alors il serait signé!), Et le maximum pourrait être 1.N <= 1
simplifie les choses. Je suppose que le type non signé n'est pas garanti, mais cela devrait être traité ailleurs, je dirais.int N
, et que vous gardiez la condition d'origine, elle se répéterait indéfiniment lorsque N est négatif avec sa condition d'origine. Puisque ce comportement n'est pas défini, nous n'avons pas vraiment besoin de nous en préoccuper. On peut donc supposer que N est non négatif, quelle que soit la déclaration.Avertissement: je ne connais pas C # et n'ai pas testé ce code:
Pas besoin de bitshifting ou autre, cela n'utilise qu'une seule comparaison, et cela devrait être beaucoup plus efficace (O (n) vs O (2 ^ n) je pense?). Le corps de la fonction est plus compact , bien qu'il finisse par être un peu plus long avec la déclaration.
(Pour supprimer la surcharge de la récursivité, il y a la version itérative, comme dans la réponse de Mathew Gunn )
PS: Il s'agit d'un modèle fonctionnel courant pour l'itération avec des accumulateurs. Si vous remplacez
N--
par,N-1
vous n'utilisez effectivement aucune mutation, ce qui le rend utilisable dans une approche purement fonctionnelle.la source
Voici ma solution, il n'y a pas grand chose à optimiser cette fonction simple, par contre ce que je propose ici, c'est la lisibilité en tant que définition mathématique de la fonction récursive.
La définition mathématique du nombre de Fibonacci d'une manière similaire.
En allant plus loin pour forcer le boîtier de commutation à créer une table de recherche.
la source
switch
lorsque vous pouvez avoir un éventail de réponses?car N est uint, il suffit d'utiliser
la source
La réponse de Dmitry est la meilleure, mais s'il s'agissait d'un type de retour Int32 et que vous disposiez d'un plus grand ensemble d'entiers, vous pourriez le faire.
la source
List.Contains
est O (n), 3) simplement faire deux comparaisons à la place (N > -3 && N < 3
) donnerait un code plus court et plus lisible.La séquence de Fibonacci est une série de nombres où un nombre est trouvé en additionnant les deux nombres qui le précèdent. Il existe deux types de points de départ: ( 0,1 , 1,2, ..) et ( 1,1 , 2,3).
La position
N
dans ce cas commence à partir de1
, ce n'est pas0-based
comme un index de tableau.En utilisant la fonction Expression-body C # 6 et la suggestion de Dmitry sur l' opérateur ternaire, nous pouvons écrire une fonction sur une ligne avec un calcul correct pour le type 1:
et pour le type 2:
la source
Un peu tard à la fête, mais tu pourrais aussi faire
(x==!!x)
!!x
convertit la valeur a en1
si ce n'est pas le cas0
et la laisse à0
si elle l'est.J'utilise beaucoup ce genre de chose dans l'obfuscation C.
Remarque: c'est C, je ne sais pas si cela fonctionne en C #
la source
uint n = 1; if (n == !!n) { }
donneOperator '!' cannot be applied to operand of type 'uint'
sur le!n
en C #. Ce n'est pas parce que quelque chose fonctionne en C que cela fonctionne en C #;#include <stdio.h>
ne fonctionne même pas en C #, car C # n'a pas la directive de préprocesseur "include". Les langages sont plus différents que le C et le C ++.J'ai donc créé un
List
de ces nombres entiers spéciaux et vérifié si cela s'yN
rapporte.Vous pouvez également utiliser une méthode d'extension à des fins différentes où elle
Contains
n'est appelée qu'une seule fois (par exemple lorsque votre application démarre et charge des données). Cela fournit un style plus clair et clarifie la relation principale avec votre valeur (N
):Appliquez-le:
Ce n'est peut-être pas le moyen le plus rapide de le faire, mais pour moi, cela semble être un meilleur style.
la source