Una pregunta que recibí en mi última entrevista:
Diseñe una función
f
, de modo que:f(f(n)) == -n
Donde
n
es un entero con signo de 32 bits ; no puedes usar aritmética de números complejos.Si no puede diseñar dicha función para todo el rango de números, diséñela para el rango más grande posible.
¿Algunas ideas?
Respuestas:
Qué tal si:
En Python:
Python promueve automáticamente enteros a longitudes arbitrarias de longitud. En otros idiomas, el número entero positivo más grande se desbordará, por lo que funcionará para todos los números enteros excepto ese.
Para que funcione para números reales, debe reemplazar la n en (-1) n con
{ ceiling(n) if n>0; floor(n) if n<0 }
.En C # (funciona para cualquier doble, excepto en situaciones de desbordamiento):
fuente
No dijiste qué tipo de lenguaje esperaban ... Aquí hay una solución estática (Haskell). Básicamente está jugando con los 2 bits más significativos:
Es mucho más fácil en un lenguaje dinámico (Python). Simplemente verifique si el argumento es un número X y devuelve una lambda que devuelve -X:
fuente
class C a b | a->b where { f :: a->b }
:;instance C Int (()->Int) where { f=const.negate }
;instance C (()->Int) Int where { f=($()) }
.Aquí hay una prueba de por qué dicha función no puede existir, para todos los números, si no utiliza información adicional (excepto 32 bits de int):
Debemos tener f (0) = 0. (Prueba: supongamos que f (0) = x. Entonces f (x) = f (f (0)) = -0 = 0. Ahora, -x = f (f (x )) = f (0) = x, lo que significa que x = 0.)
Además, para cualquiera
x
yy
, supongamosf(x) = y
. Queremosf(y) = -x
entonces. Yf(f(y)) = -y => f(-x) = -y
. Para resumir: sif(x) = y
, entoncesf(-x) = -y
, yf(y) = -x
, yf(-y) = x
.Entonces, necesitamos dividir todos los enteros excepto 0 en conjuntos de 4, pero tenemos un número impar de tales enteros; no solo eso, si eliminamos el número entero que no tiene una contraparte positiva, todavía tenemos 2 números (mod4).
Si eliminamos los 2 números máximos restantes (por valor de abs), podemos obtener la función:
Por supuesto, otra opción es no cumplir con 0 y obtener los 2 números que eliminamos como bonificación. (Pero eso es una tontería si).
fuente
n = -2147483648
(valor mínimo); no puedeabs(n)
en ese caso, y el resultado será indefinido (o una excepción).Gracias a la sobrecarga en C ++:
fuente
O puede abusar del preprocesador:
fuente
Esto es cierto para todos los números negativos.
Debido a que hay un número negativo más que números positivos para dos enteros complementarios,
f(n) = abs(n)
es válido para un caso más que unaf(n) = n > 0 ? -n : n
solución que sea la misma quef(n) = -abs(n)
. Te tengo por uno ...: DACTUALIZAR
No, no es válido para un caso más, como acabo de reconocer por el comentario de litb ...
abs(Int.Min)
simplemente se desbordará ...También pensé en usar la información de mod 2, pero concluí que no funciona ... demasiado pronto. Si se hace correctamente, funcionará para todos los números, excepto
Int.Min
porque se desbordará.ACTUALIZAR
Jugué con él por un tiempo, buscando un buen truco de manipulación, pero no pude encontrar una buena línea, mientras que la solución mod 2 encaja en una.
En C #, esto se convierte en lo siguiente:
Para conseguir que funcione para todos los valores, tiene que sustituir
Math.Abs()
con(n > 0) ? +n : -n
e incluir el cálculo en ununchecked
bloque. Luego, incluso teInt.Min
asignas a ti mismo como lo hace la negación sin control.ACTUALIZAR
Inspirado por otra respuesta, voy a explicar cómo funciona la función y cómo construirla.
Comencemos desde el principio. La función
f
se aplica repetidamente a un valor dado quen
produce una secuencia de valores.La pregunta exige
f(f(n)) = -n
, es decir, dos aplicaciones sucesivas def
negar el argumento. Dos aplicaciones adicionales def
- cuatro en total - niegan el argumento nuevamente dando como resultadon
nuevamente.Ahora hay un ciclo obvio de longitud cuatro. Sustituyendo
x = f(n)
y observando que la ecuación obtenida sef(f(f(n))) = f(f(x)) = -x
cumple, se obtiene lo siguiente.Entonces obtenemos un ciclo de longitud cuatro con dos números y los dos números negados. Si imagina el ciclo como un rectángulo, los valores negados se encuentran en las esquinas opuestas.
Una de las muchas soluciones para construir dicho ciclo es la siguiente a partir de n.
Un ejemplo concreto es de tal ciclo es
+1 => -2 => -1 => +2 => +1
. Casi terminamos. Al observar que el ciclo construido contiene un número positivo impar, su sucesor par, y ambos números se niegan, podemos dividir fácilmente los enteros en muchos de estos ciclos (2^32
es un múltiplo de cuatro) y hemos encontrado una función que satisface las condiciones.Pero tenemos un problema con cero. El ciclo debe contener
0 => x => 0
porque el cero se niega a sí mismo. Y porque el ciclo ya dice0 => x
que sigue0 => x => 0 => x
. Este es solo un ciclo de longitud dos yx
se convierte en sí mismo después de dos aplicaciones, no en-x
. Afortunadamente, hay un caso que resuelve el problema. SiX
es igual a cero, obtenemos un ciclo de longitud uno que contiene solo cero y resolvemos ese problema concluyendo que cero es un punto fijo def
.¿Hecho? Casi. Tenemos
2^32
números, el cero es un punto fijo que deja2^32 - 1
números, y debemos dividir ese número en ciclos de cuatro números. Malo que2^32 - 1
no es un múltiplo de cuatro: quedarán tres números que no estarán en ningún ciclo de longitud cuatro.Explicaré la parte restante de la solución usando el conjunto más pequeño de iteradores con signo de 3 bits que van desde
-4
hasta+3
. Hemos terminado con cero. Tenemos un ciclo completo+1 => -2 => -1 => +2 => +1
. Ahora construyamos el ciclo comenzando en+3
.El problema que surge es que
+4
no es representable como un entero de 3 bits. Obtendríamos+4
negando-3
a+3
- lo que todavía es un número entero de 3 bits válido - pero luego sumando uno al+3
(binario011
) los rendimientos100
binario. Se interpreta como un entero sin signo,+4
pero tenemos que interpretarlo como un entero con signo-4
. Entonces, en realidad,-4
para este ejemplo oInt.MinValue
en el caso general, hay un segundo punto fijo de negación aritmética de enteros,0
yInt.MinValue
se asignan a sí mismos. Entonces el ciclo es en realidad como sigue.Es un ciclo de longitud dos y además
+3
ingresa al ciclo a través de-4
. En consecuencia,-4
se asigna correctamente a sí mismo después de dos aplicaciones de función,+3
se asigna correctamente-3
después de dos aplicaciones de función, pero-3
se asigna erróneamente a sí mismo después de dos aplicaciones de función.Así que construimos una función que funciona para todos los enteros menos uno. ¿Podemos hacerlo mejor? No podemos. ¿Por qué? Tenemos que construir ciclos de longitud cuatro y podemos cubrir todo el rango entero hasta cuatro valores. Los valores restantes son los dos puntos fijos
0
yInt.MinValue
que deben asignarse a sí mismos y a dos enteros arbitrariosx
y-x
deben correlacionarse entre sí mediante dos aplicaciones de función.Para asignar
x
a-x
y viceversa deben formar un ciclo de cuatro y deben estar ubicados en las esquinas opuestas de ese ciclo. En consecuencia0
yInt.MinValue
tiene que estar en las esquinas opuestas, también. Esto asignará correctamentex
y cambiará-x
los dos puntos fijos0
yInt.MinValue
después de dos aplicaciones de función y nos dejará con dos entradas fallidas. Por lo tanto, no es posible construir una función que funcione para todos los valores, pero tenemos una que funciona para todos los valores excepto uno y esto es lo mejor que podemos lograr.fuente
Usando números complejos, puede dividir efectivamente la tarea de negar un número en dos pasos:
Lo bueno es que no necesita ningún código de manejo especial. Simplemente multiplicando por i hace el trabajo.
Pero no puedes usar números complejos. Entonces, de alguna manera, debe crear su propio eje imaginario, utilizando parte de su rango de datos. Como necesita exactamente tantos valores imaginarios (intermedios) como valores iniciales, solo le queda la mitad del rango de datos.
Traté de visualizar esto en la siguiente figura, suponiendo datos firmados de 8 bits. Tendría que escalar esto para enteros de 32 bits. El rango permitido para n inicial es -64 a +63. Esto es lo que hace la función para n positivo:
Para n negativo, la función usa el rango intermedio -65 ..- 128.
fuente
float
vsint
). El 'anillo de 4 elementos' que describen muchas respuestas necesita 4 estados, que se pueden representar como 2 dimensiones, cada una con 2 estados. El problema con esta respuesta es que requiere espacio de procesamiento adicional (solo 'funciona' para -64..63, pero necesita -128..127 espacio) y no establece explícitamente la fórmula escrita.Funciona excepto int.MaxValue y int.MinValue
fuente
0
a0
y-2147483648
a-2147483648
puntos ya que estos dos números son fijos para el operador de negación,x => -x
. Para el resto de los números, siga las flechas en la imagen de arriba. Como queda claro por la respuesta de SurDin y sus comentarios, habrá dos números, en este caso2147483647
y-2147483647
sin otro par que intercambiar.La pregunta no dice nada acerca de lo que el tipo de entrada y el valor de retorno de la función
f
tienen que ser (al menos no de la manera que has presentado) ...... solo que cuando n es un entero de 32 bits
f(f(n)) = -n
Entonces, ¿qué tal algo como
Si n es un entero de 32 bits, la declaración
f(f(n)) == -n
será verdadera.Obviamente, este enfoque podría extenderse para que funcione para un rango aún más amplio de números ...
fuente
para javascript (u otros lenguajes escritos dinámicamente) puede hacer que la función acepte un int o un objeto y devuelva el otro. es decir
dando
alternativamente, podría usar la sobrecarga en un lenguaje fuertemente tipado, aunque eso puede romper las reglas
fuente
Dependiendo de su plataforma, algunos idiomas le permiten mantener el estado en la función. VB.Net, por ejemplo:
IIRC, C ++ permitió esto también. Sin embargo, sospecho que están buscando una solución diferente.
Otra idea es que, dado que no definieron el resultado de la primera llamada a la función, podría usar impar / par para controlar si invertir el signo:
Suma uno a la magnitud de todos los números pares, resta uno de la magnitud de todos los números impares. El resultado de dos llamadas tiene la misma magnitud, pero en una llamada donde incluso cambiamos el signo. Hay algunos casos en los que esto no funcionará (-1, max o min int), pero funciona mucho mejor que cualquier otra cosa sugerida hasta ahora.
fuente
Explotación de excepciones de JavaScript.
fuente
Para todos los valores de 32 bits (con la advertencia de que -0 es -2147483648)
Básicamente, necesita emparejar cada bucle -x => x => -x con ay => -y => bucle y. Así que emparejé lados opuestos de la
split
.Por ejemplo, para enteros de 4 bits:
fuente
Una versión de C ++, probablemente doblando las reglas, pero funciona para todos los tipos numéricos (flotantes, ints, dobles) e incluso tipos de clase que sobrecargan el menos unario:
fuente
x86 asm (estilo AT&T):
Código verificado, todos los enteros posibles de 32 bits pasados, error con -2147483647 (flujo inferior).
fuente
Usa globals ... pero ¿y eso?
fuente
Esta solución de Perl funciona para enteros, flotantes y cadenas .
Prueba algunos datos de prueba.
Salida:
fuente
n
fuera una cadena, podría hacer que 548 se convierta en "First_Time_548" y luego la próxima vez que se ejecute a través de la función ... if (prefix == First_Time_ ") reemplaza" First_Time_ "con" - "Nadie dijo que f (x) tenía que ser del mismo tipo.
fuente
Realmente no estoy tratando de dar una solución al problema en sí, pero tengo un par de comentarios, ya que la pregunta dice que este problema fue parte de una entrevista (de trabajo):
int.MinValue
aint.MaxValue
, y para cadan
llamada dentro de ese rangof(f(n))
y verificar el resultado es-n
), diciéndole que luego usaría Test Driven Development para llegar a tal función.Oh, esta respuesta asume que la entrevista fue para un puesto relacionado con la programación de C #. Por supuesto, sería una respuesta tonta si la entrevista fuera para un puesto relacionado con las matemáticas. ;-)
fuente
Me gustaría cambiar los 2 bits más significativos.
Como puede ver, es solo una adición, dejando fuera el bit llevado.
¿Cómo llegué a la respuesta? Mi primer pensamiento fue solo una necesidad de simetría. 4 vueltas para volver a donde empecé. Al principio pensé, ese es el código Gray de 2 bits. Entonces pensé que en realidad el binario estándar es suficiente.
fuente
Aquí hay una solución inspirada en el requisito o la afirmación de que los números complejos no se pueden usar para resolver este problema.
Multiplicar por la raíz cuadrada de -1 es una idea, que solo parece fallar porque -1 no tiene una raíz cuadrada sobre los enteros. Pero jugar con un programa como Mathica da, por ejemplo, la ecuación
y esto es casi tan bueno como tener una raíz cuadrada de -1. El resultado de la función debe ser un entero con signo. Por lo tanto, voy a usar un modulo modificado mods de operación (x, n) que devuelve el entero y congruente a x módulo n que está más cerca de 0. Solo muy pocos lenguajes de programación tienen una operación de módulo similar, pero se puede definir fácilmente . Por ejemplo, en python es:
Usando la ecuación anterior, el problema ahora se puede resolver como
Esto satisface
f(f(x)) = -x
a todos los enteros en el rango . Los resultados de también están en este rango, pero por supuesto el cálculo necesitaría enteros de 64 bits.[-2
31
-2, 2
31
-2]
f(x)
fuente
C # para un rango de 2 ^ 32 - 1 números, todos los números int32 excepto (Int32.MinValue)
huellas dactilares:
fuente
Esencialmente, la función tiene que dividir el rango disponible en ciclos de tamaño 4, con -n en el extremo opuesto del ciclo de n. Sin embargo, 0 debe ser parte de un ciclo de tamaño 1, porque de lo contrario
0->x->0->x != -x
. Debido a que 0 está solo, debe haber otros 3 valores en nuestro rango (cuyo tamaño es un múltiplo de 4) que no están en un ciclo adecuado con 4 elementos.Elegí estos valores extraños adicionales a ser
MIN_INT
,MAX_INT
yMIN_INT+1
. Además, seMIN_INT+1
asignaráMAX_INT
correctamente, pero se quedará atascado allí y no se asignará de nuevo. Creo que este es el mejor compromiso, porque tiene la buena propiedad de que solo los valores extremos no funcionan correctamente. Además, significa que funcionaría para todos los BigInts.fuente
Nadie dijo que tenía que ser apátrida.
Hacer trampa, pero no tanto como muchos de los ejemplos. Aún más mal sería mirar la pila para ver si la dirección de la persona que llama es & f, pero esto será más portátil (aunque no seguro para subprocesos ... la versión segura para subprocesos usaría TLS). Aún más malvado:
Por supuesto, ninguno de estos funciona demasiado bien para el caso de MIN_INT32, pero hay poco que pueda hacer al respecto a menos que se le permita devolver un tipo más amplio.
fuente
Me imagino que usar el bit 31 como bit imaginario ( i ) sería un enfoque que admitiría la mitad del rango total.
fuente
funciona para n = [0 .. 2 ^ 31-1]
fuente
El problema establece "enteros con signo de 32 bits", pero no especifica si son dos-complemento o unos-complemento .
Si usa complementos de uno, entonces todos los valores de 2 ^ 32 ocurren en ciclos de longitud cuatro: no necesita un caso especial para cero, y tampoco necesita condicionales.
C ª:
Esto funciona por
Después de dos pasadas, tenemos el inverso en bits del valor original. Que en la representación del complemento uno es equivalente a la negación.
Ejemplos:
fuente
:RE
fuente
fuente
Me gustaría compartir mi punto de vista sobre este interesante problema como matemático. Creo que tengo la solución más eficiente.
Si no recuerdo mal, niegas un entero de 32 bits con signo simplemente volteando el primer bit. Por ejemplo, si n = 1001 1101 1110 1011 1110 0000 1110 1010, entonces -n = 0001 1101 1110 1011 1110 0000 1110 1010.
Entonces, ¿cómo definimos una función f que toma un entero de 32 bits con signo y devuelve otro entero de 32 bits con la propiedad de que tomar f dos veces es lo mismo que voltear el primer bit?
Permítanme reformular la pregunta sin mencionar conceptos aritméticos como enteros.
¿Cómo definimos una función f que toma una secuencia de ceros y unos de longitud 32 y devuelve una secuencia de ceros y unos de la misma longitud, con la propiedad de que tomar f dos veces es lo mismo que voltear el primer bit?
Observación: Si puede responder la pregunta anterior para el caso de 32 bits, también puede responder para el caso de 64 bits, el caso de 100 bits, etc. Simplemente aplique f al primer bit de 32 bits.
Ahora si puedes responder la pregunta para el caso de 2 bits, ¡Voila!
Y sí, resulta que cambiar los primeros 2 bits es suficiente.
Aquí está el pseudocódigo
Observación: El paso 2 y el paso 3 juntos se pueden resumir como (a, b) -> (-b, a). ¿Luce familiar? Eso debería recordarle la rotación de 90 grados del plano y la multiplicación por la raíz cuadrada de -1.
Si solo presentara el pseudocódigo solo sin el largo preludio, parecería un conejo fuera del sombrero, quisiera explicar cómo obtuve la solución.
fuente