Así que estaba tratando de escribir el número n en la secuencia de Fibonacci en una función lo más compacta posible:
public uint fibn ( uint N )
{
return (N == 0 || N == 1) ? 1 : fibn(N-1) + fibn(N-2);
}
Pero me pregunto si puedo hacer esto aún más compacto y eficiente cambiando
(N == 0 || N == 1)
en una sola comparación. ¿Hay alguna operación de cambio de bits elegante que pueda hacer esto?
c#
algorithm
optimization
arithmetic-expressions
user6048670
fuente
fuente
fibn(N-1) + fibn(N-2)
lugar deN * fibn(N-1)
?Respuestas:
Este también funciona
La raíz cuadrada de 0 y 1 devolverá 0 y 1 respectivamente.
fuente
Math.Sqrt
es una función complicada de punto flotante. ¡Se ejecuta lentamente en comparación con las alternativas de solo enteros!Hay varias formas de implementar su prueba aritmética usando aritmética bit a bit. Tu expresión:
x == 0 || x == 1
es lógicamente equivalente a cada uno de estos:
(x & 1) == x
(x & ~1) == 0
(x | 1) == 1
(~x | 1) == (uint)-1
x >> 1 == 0
Prima:
x * x == x
(la prueba requiere un poco de esfuerzo)Pero en la práctica, estos formularios son los más legibles, y la pequeña diferencia en el rendimiento no vale la pena usar aritmética bit a bit:
x == 0 || x == 1
x <= 1
(porquex
es un entero sin signo)x < 2
(porquex
es un entero sin signo)fuente
(x & ~1) == 0
x == 0 || x == 1
para(x & ~1) == 0
o para(x | 1) == 1
. Para el primero, es lo suficientemente inteligente como para reconocerlo como equivalentex <= 1
y generar un archivocmpl; setbe
. Los demás lo confunden y hacen que genere peor código.Dado que el argumento está
uint
( sin firmar ) puede ponerMenos legible (en mi humilde opinión) pero si cuentas cada carácter ( Code Golf o similar)
Editar : para su pregunta editada :
O
fuente
return N<2?1:f(N-1)+f(n-2)
. : PTambién puede verificar que todos los demás bits sean 0 así:
Para completar gracias a Matt, la solución aún mejor:
En ambos casos, debe cuidar los paréntesis porque los operadores bit a bit tienen una prioridad menor que
==
.fuente
(N|1)==1
Si lo que desea hacer es hacer que la función sea más eficiente, utilice una tabla de búsqueda. La tabla de búsqueda es sorprendentemente pequeña con solo 47 entradas; la siguiente entrada desbordaría un entero sin signo de 32 bits. Por supuesto, también hace que escribir la función sea trivial.
Obviamente, puedes hacer lo mismo con los factoriales.
fuente
Cómo hacerlo con bitshift
Si desea usar bitshift y hacer que el código sea algo oscuro (pero corto), puede hacer:
Para un entero sin signo
N
en el lenguaje c,N>>1
descarta el bit de orden inferior. Si ese resultado es distinto de cero, implica que N es mayor que 1.Nota: este algoritmo es terriblemente ineficiente ya que recalcula innecesariamente valores en la secuencia que ya se han calculado.
Algo MUCHO MUCHO más rápido
Calcule una pasada en lugar de construir implícitamente un árbol del tamaño de fibonaci (N):
Como han mencionado algunas personas, no se tarda mucho en desbordar incluso un entero de 64 bits sin signo. Dependiendo de qué tan grande esté tratando de llegar, deberá usar números enteros de precisión arbitrarios.
fuente
uint
no se puede convertir implícitamente enbool
, y la pregunta está etiquetada específicamente como C #.--N != 0
lugar. El punto es que algo O (n) es preferible a O (fibn (n)).Como usa un uint, que no puede ser negativo, puede verificar si
n < 2
EDITAR
O para ese caso de función especial, podría escribirlo de la siguiente manera:
lo que conducirá al mismo resultado, por supuesto, a costa de un paso de recursividad adicional.
fuente
1 * fibn(0) = 1 * 1 = 1
fibn
entoncesSimplemente verifique si
N
es <= 1 ya que sabe que N no está firmado, solo puede haber 2 condiciones que denN <= 1
como resultadoTRUE
: 0 y 1fuente
(N == 0 || N == 1)
. Sabes que no será menor que 0 (¡porque entonces estaría firmado!), Y el máximo podría ser 1. loN <= 1
simplifica. Supongo que el tipo sin firmar no está garantizado, pero eso debería manejarse en otro lugar, diría yo.int N
y se mantuviera la condición original, se repetirá infinitamente cuando N sea negativo con su condición original. Dado que ese es un comportamiento indefinido, en realidad no tenemos que preocuparnos por ello. Entonces podemos asumir que N no es negativo, independientemente de la declaración.Descargo de responsabilidad: no sé C # y no probé este código:
No es necesario el cambio de bits o algo así, esto usa solo una comparación, y debería ser mucho más eficiente (O (n) vs O (2 ^ n), ¿creo?). El cuerpo de la función es más compacto , aunque termina siendo un poco más largo con la declaración.
(Para eliminar la sobrecarga de la recursividad, existe la versión iterativa, como en la respuesta de Mathew Gunn )
PD: Este es un patrón funcional común para la iteración con acumuladores. Si reemplaza
N--
conN-1
, efectivamente no está usando ninguna mutación, lo que lo hace utilizable en un enfoque funcional puro.fuente
Aquí está mi solución, no hay mucho para optimizar esta función simple, por otro lado, lo que estoy ofreciendo aquí es la legibilidad como una definición matemática de la función recursiva.
La definición matemática del número de Fibonacci de manera similar.
Llevándolo más lejos para forzar a la caja del interruptor a construir una tabla de búsqueda.
fuente
switch
cuando puedes tener una variedad de respuestas?para N es uint, solo use
fuente
La respuesta de Dmitry es la mejor, pero si fuera un tipo de retorno Int32 y tuviera un conjunto más grande de enteros para elegir, podría hacer esto.
fuente
List.Contains
es O (n), 3) simplemente hacer dos comparaciones en su lugar (N > -3 && N < 3
) daría un código más corto y más legible.La secuencia de Fibonacci es una serie de números donde un número se encuentra sumando los dos números anteriores. Hay dos tipos de puntos de partida: ( 0,1 , 1,2, ..) y ( 1,1 , 2,3).
La posición
N
en este caso comienza desde1
, no es0-based
como un índice de matriz.Usando la función de cuerpo de expresión de C # 6 y la sugerencia de Dmitry sobre el operador ternario, podemos escribir una función de una línea con el cálculo correcto para el tipo 1:
y para el tipo 2:
fuente
Un poco tarde para la fiesta, pero también podrías hacerlo
(x==!!x)
!!x
convierte el valor a1
si no lo es0
, y lo deja0
si lo es.Utilizo mucho este tipo de cosas en la ofuscación de C.
Nota: Esto es C, no estoy seguro si funciona en C #
fuente
uint n = 1; if (n == !!n) { }
daOperator '!' cannot be applied to operand of type 'uint'
en el!n
en C #. El hecho de que algo funcione en C no significa que funcione en C #; incluso#include <stdio.h>
no funciona en C #, porque C # no tiene la directiva de preprocesador "incluir". Los lenguajes son más diferentes que C y C ++.Así que creé uno
List
de estos enteros especiales y verifiqué siN
pertenece a él.También puede usar un método de extensión para diferentes propósitos donde
Contains
se llama solo una vez (por ejemplo, cuando su aplicación está iniciando y cargando datos). Esto proporciona un estilo más claro y aclara la relación principal con su valor (N
):Apliquelo:
Puede que esta no sea la forma más rápida de hacerlo, pero para mí, parece ser un estilo mejor.
fuente