Adición en base -1 + i

64

Los enteros gaussianos son números complejos de la forma a+bidonde ay bson ambos enteros. En la base -1 + i, todos los enteros gaussianos se pueden representar de forma única utilizando los dígitos 0y 1, sin la necesidad de un símbolo para indicar el signo.

Por ejemplo, 1100en base -1 + i representa el número decimal 2, ya que

1*(-1+i)^3 + 1*(-1+i)^2 + 0*(-1+i)^1 + 0*(-1+i)^0
= (2+2i) + (-2i) + 0 + 0
= 2

La entrada será dos enteros gaussianos en base -1 + i representados usando los dígitos 01. Esto puede tomar una de las siguientes formas:

  • Dos cadenas de dígitos separadas,
  • Dos enteros decimales que consisten en 01representar los números base -1 + i (por ejemplo, 1100para 2 en base -1 + i),
  • Dos enteros binarios que representan los números base -1 + i (por ejemplo, decimal 12o 0b1100para 2 en base -1 + i)
  • Una sola cadena que separa cadenas de dos dígitos / enteros binarios por un solo separador no alfanumérico (por ejemplo, 1100 1100o 12,12para 2 + 2)

Genere la suma de los dos enteros gaussianos, también en base -1 + i y representados usando los dígitos 01(en uno de los formatos permitidos como entrada, no necesariamente la misma opción). La salida puede contener un número finito de ceros a la izquierda.

Su función o programa debe finalizar en 2 segundos para entradas de 30 dígitos como máximo cada una.

Aclaraciones adicionales

  • Puede suponer que la entrada no contiene ceros iniciales extraños. Para el caso especial de 0, puede elegir una 0cadena vacía o la representación.

Casos de prueba

0, 0 => 0                                      # 0 + 0 = 0
0, 1 => 1                                      # 0 + 1 = 1
1, 1 => 1100                                   # 1 + 1 = 2
1100, 1100 => 111010000                        # 2 + 2 = 4
1101, 1101 => 111011100                        # 3 + 3 = 6
110111001100, 1110011011100 => 0               # 42 + (-42) = 0
11, 111 => 0                                   # i + (-i) = 0
11, 110 => 11101                               # i + (-1-i) = -1
10101, 11011 => 10010                          # (-3-2i) + (-2+3i) = (-5+i)
1010100101, 111101 => 1110100000100            # (-19+2i) + (3-4i) = (-16-2i)

Casos de prueba más largos:

11011011010110101110010001001, 111100010100101001001010010101 => 0
111111111111111111111111111111, 111111111111111111111111111111 => 100100100100100100100100100100
101101110111011101110111011101, 101101110111011101110111011101 => 11101001010001000100010001000100011100
100100010101001101010110101010, 100010011101001011111110101000 => 110000110010101100001100111100010
Sp3000
fuente
¿No hay listas de dígitos?
CalculatorFeline
@CatsAreFluffy No hay listas de dígitos, lo siento.
Sp3000
96
Puede guardar un byte cambiando -1+ia i-1en el título.
mbomb007
1
Ahora necesitamos una conversión al revés. : P
Rɪᴋᴇʀ
3
Hay 1100 tipos de personas en el mundo. Los que entienden binario, los que no, los que lo confunden con ternario, los que lo confunden con la base 4, los que lo confunden con la base 5, los que lo confunden con la base -1 + i, los que lo confunden con base 6, los que lo confunden con la base 7, los que lo confunden con la base 8, los que lo confunden con la base 9 ...
wizzwizz4

Respuestas:

42

Python 2, 98 97 91 84 bytes

s=input();L=1
for _ in`s`*8:s+=1098*int(str(s).translate('0011'*64));L*=10
print s%L

Esto hace E / S en decimal. Los enteros deben estar separados por el carácter no alfanumérico +.

¡Gracias a @xnor por jugar 2 bytes!

Pruébalo en Ideone .

Cómo funciona

En Aritmética en bases complejas , el autor muestra cómo sumar y multiplicar números complejos en bases de la forma -n + i .

Para la base -1 + i , la suma se realiza de manera similar a la suma binaria regular con carry, con dos diferencias:

  • En lugar de llevar 1 a la siguiente posición más alta, llevamos 110 a las siguientes tres.

  • Los dígitos de transporte pueden propagarse indefinidamente. Sin embargo, sin ceros a la izquierda, la suma a + b tiene como máximo ocho dígitos más que el máximo de a y b .

Procedemos de la siguiente manera.

  1. En primer lugar, añadimos una y b como si sus cifras eran dígitos decimales.

    Para a = 10,101 y b = 11,011 , esto da 21.112 .

  2. A continuación, formamos un nuevo número reemplazando los dígitos mayores que 1 con un 1 , otros con un 0 . 1

    Para la suma 21112 , esto da 10001 .

  3. Para cada dígito mayor que 1 , tenemos que restar 2 de ese dígito y llevar 110 a las siguientes tres posiciones más altas. Como 1098 = 10 * 110 - 2 , podemos lograr esto multiplicando el resultado del paso 2 por 1098 , y luego sumando ese producto a la suma. 2

    Para la suma 21112 , esto da 21112 + 1098 * 10001 = 21112 + 10981098 = 11002210 .

  4. Repetimos los pasos 2 y 3 un total de d * 8 veces, donde d es el número de dígitos de a + b . 3

    Para la suma inicial 21112 , los resultados son

                          11002210
                          12210010
                        1220010010
                      122000010010
                    12200000010010
                  1220000000010010
                122000000000010010
              12200000000000010010
            1220000000000000010010
          122000000000000000010010
        12200000000000000000010010
      1220000000000000000000010010
    122000000000000000000000010010
                                 .
                                 .
                                 .
    
  5. Tomamos el módulo de suma final 10 d + 8 , descartando todos excepto los últimos d + 8 dígitos.

    Para la suma inicial 21112 , el resultado final es 10010 .


1 Esto se logra con traducir . Repetir la cadena 0011 64 veces hace que una repetición se alinee con la secuencia de caracteres ASCII 0123 , logrando el reemplazo deseado.

2 Tenga en cuenta que los dígitos de esta suma no pueden exceder 3 (valor inicial 1 más dos 1 'de acarreos).

3 Esto funciona para d = 1 , y d * 8> d + 8 de lo contrario. El código puede repetir los pasos (d + 1) * 8 veces, ya que s tiene una L final si s es un entero largo .

Dennis
fuente
77
Esto es magia profunda . ¿Qué formato input()espera? (Recibo 21112cuando ingreso 10101, 11011.)
Tim Pederick
1
No importa; que ejecutaba una versión traducida (sin éxito, al parecer) a Python 3. Funciona bien en Python 2 .
Tim Pederick el
99
...Cómo. Por favor. Explique.
Nic Hartley
@QPaysTaxes He editado mi respuesta.
Dennis
@ Dennis Ahora, ¿podría explicar por qué eso funciona? Por ejemplo, ¿por qué d+8y no, por ejemplo d+9? ¿¿¿¿Cómo????
Nic Hartley
16

Pyth, 34 bytes

_shM.u,%J/eMeN\12-+PMeNm.B6/J2k,kQ

Pruébelo en línea: Demostración o Test Suite (lleva bastante tiempo). Debería satisfacer la restricción de tiempo aunque fácilmente, ya que el compilador en línea es bastante lento en comparación con el compilador normal (fuera de línea).

Explicación:

Mi algoritmo es básicamente una implementación de adición con transporte. Pero en lugar de llevar 1, tengo que llevar 110( 1100en la base -1+ies lo mismo que 2en la base -1+i). Esto funciona principalmente bien, pero puede quedar atrapado en un bucle de impresión de ceros infinitos. Por ejemplo, si está agregando 1con 11y actualmente tiene el carry 110. Así que básicamente agrego hasta quedar atrapado en un bucle y luego me detengo. Creo que un bucle que un bucle siempre imprimirá ceros y, por lo tanto, esto debería estar bien.

_shM.u,%J/eMeN\12-+PMeNm.B6/J2k,kQ   implicit: Q = input list of strings
                               ,kQ   create the pair ["", Q]
    .u                               modify the pair N (^) until loop:
      ,                                replace N with a new pair containing:
            eN                           N[1] (the remaining summand)
          eM                             take the last digits of each summand
         /    \1                         count the ones
        J                                store the count in J
       %J       2                        J % 2 (this is the first element of the new pair)
                   PMeN                  remove the last digit of each summand
                  +    m   /J2           and add J / 2 new summand:
                        .B6                 with the value "110" (binary of 6)
                 -            k          remove empty summand
    .u                               returns all intermediate results
  hM                                 extract the digits
 s                                   sum them up to a long string
_                                    reverse
Jakube
fuente
13

Python 2, 69 67 bytes

f=lambda a,b:a*a+b*b^58and 2*f(a*b%2*6,f(a/2,b/2))|a+b&1if a else b

La E / S se realiza con enteros de base 2.

-2 gracias @Dennis.

Feersum
fuente
Lo tomo a*a+b*b^58==0cuando ay bson inversas? ¿Cómo funciona?
xnor
@xnor No, a*a+b*b==58cuando uno de ellos es 3 y el otro es 7.
feersum
1
No es obvio que ese (3,7)es el único par que da un ciclo y necesita una carcasa especial. Si es cierto, entonces solo necesita verificar (a,b)==(3,7)en ese orden, ya que (7,3)recurre a (3,7), y tal vez haya una expresión más corta para eso.
xnor
1
Ahora esto está garantizado para confundir a cualquier persona que no sabe (o se olvida) que (a) ^es XOR, no exponenciación, o (b) XOR tiene menor prioridad que +.
Tim Pederick
12

Retina , 100 bytes

r+`(.*)(\d|(?!\4))( .*)(.?)
$2$4:$1$3
T` 0
+`1:11(1*:1*)11
:$1
^:*
:::
}`:(1*:1*:)11
1:1$1
(1)*:
$#1

Esto toma la entrada separada con una coma. La salida siempre comienza con tres ceros a la izquierda.

Pruébalo en línea!

Realmente me pregunto si hay una solución más corta para la primera etapa ...

Martin Ender
fuente
2
No, no, el puntaje es perfecto como es;)
Conor O'Brien
2
Buen puntaje de -2i!
Nic Hartley
Guau. No vi esta solución cuando publiqué la mía ... Mucho más superior que mi solución.
Leaky Nun
@KennyLau Estaba mirándolo y pensando "hm, creo que debería haber agregado una explicación en algún momento ..."
Martin Ender
...- 2i? Esto es decimal pero el programa usa una base que no.
user75200
12

Gelatina, 29 28 26 24 21 20 bytes

DBḅ1100ḌµDL+8µ¡Dṣ2ṪḌ

Esto hace E / S en decimal. Los enteros deben estar separados por el carácter no alfanumérico +.

Pruébalo en línea! o verificar todos los casos de prueba .

Antecedentes

En Aritmética en bases complejas , el autor muestra cómo sumar y multiplicar números complejos en bases de la forma -n + i .

Para la base -1 + i , la suma se realiza de manera similar a la suma binaria regular con carry, con dos diferencias:

  • En lugar de llevar 1 a la siguiente posición más alta, llevamos 110 a las siguientes tres.

  • Los dígitos de transporte pueden propagarse indefinidamente. Sin embargo, sin ceros a la izquierda, la suma a + b tiene como máximo ocho dígitos más que el máximo de a y b .

Procedemos de la siguiente manera.

  1. En primer lugar, añadimos una y b como si sus cifras eran dígitos decimales.

    Para a = 10,101 y b = 11,011 , esto da 21.112 .

  2. Para cada dígito mayor que 1 , tenemos que restar 2 de ese dígito y llevar 110 a las siguientes tres posiciones más altas. Podemos lograr esto al convertir cada dígito decimal a binario, las matrices binarias resultantes de base 1100 a entero, e interpretar la lista resultante de 0 's, 1 ' s, 1100 's y 1101 ' s como una base no canónica 10 número. 1

    Para la suma 21112 , esto da 21112 + 1098 * 10001 = 21112 + 10981098 = 11002210 .

  3. Repetimos los pasos 2 un total de d + 8 veces, donde d es el número de dígitos de a + b .

    Para la suma inicial 21112 , los resultados son

                          11002210
                          12210010
                        1220010010
                      122000010010
                    12200000010010
                  1220000000010010
                122000000000010010
              12200000000000010010
            1220000000000000010010
          122000000000000000010010
        12200000000000000000010010
      1220000000000000000000010010
    122000000000000000000000010010
    
  4. Descartamos todos menos los últimos d + 8 dígitos del resultado final. Esto se logra descartando todo después de los últimos 2 . 2

    Para la suma inicial 21112 , el resultado final es 10010 .

Cómo funciona

DBḅ1100ḌµDL+8µ¡Dṣ2ṪḌ  Main link. Argument: a + b (implicit sum)

        µ    µ¡       Execute the chain before the first µ n times, where n is
                      the result of executing the chain before the second µ.
         D            Convert a + b to base 10.
          L           Length; count the decimal digits.
           +8         Add 8 to the number of digits.
D                     Convert the initial/previous sum to base 10.
 B                    Convert each digit (0 - 3) to binary.
  ḅ1100               Convert each binary array from base 1100 to integer.
       Ḍ              Interpret the resulting list as a base 10 number.
               D      Convert the final sum to base 10.
                ṣ2    Split at occurrences of 2.
                  Ṫ   Select the last chunk.
                   Ḍ  Convert from base 10 to integer.

1 Tenga en cuenta que los dígitos de esta suma no pueden exceder 3 (valor inicial 1 más dos 1 'de acarreos).

2 Esto funciona porque el último dígito que se cancelará no puede ser un 3 .

Dennis
fuente
6

Python 3, 289 bytes

Esto lleva a cabo la suma de dígitos a dígitos de menor a mayor importancia (en otras palabras, exactamente el mismo algoritmo que le enseñaron en la escuela primaria). Las diferencias son que (a) está en binario, no decimal, por lo que llevas siempre que un dígito sea 2 o más, y (b) 1 + 1 = 1100, no 10.

En realidad, también es necesario tener en cuenta que 11 + 111 = 0, de lo contrario, las sumas que deberían convertirse en cero nunca terminarán.

from collections import*
def a(*s,p=0):
 r=defaultdict(int,{0:0})
 for S in s:
  n=0
  for d in S[::-1]:r[n]+=d=='1';n+=1
 while p<=max(r):
  while r[p]>1:
   r[p]-=2
   if r[p+1]>1<=r[p+2]:r[p+1]-=2;r[p+2]-=1
   else:r[p+2]+=1;r[p+3]+=1
  p+=1
 return str([*map(r.get,sorted(r))])[-2::-3]

Más golf es seguramente posible.

Tim Pederick
fuente
¿Qué tan seguro está de que su "detector cero" es suficiente?
Yakk
44
@Yakk: En una escala de uno a una revista revisada por pares, ¿tal vez le des un contraejemplo todavía?
Tim Pederick
2

Retina, 157 151 134 133 124 123 bytes

1 byte apagado gracias a Martin Büttner.

(.+),(.+)
$.1$*0$2,$.2$*0$1,
1
0x
+`(0x*)(,.*)0(x*),
$2,$1$3
{`,

(^|0x0xx0xx)
000
(0x*)(0x*)(0x*0)xx
$1x$2x$3
)`^0+
0
0x
1

Pruébalo en línea!

Convierte a unario y luego repite los siguientes reemplazos (mostrados aquí en decimal):

122 -> 000
0002 -> 1100 (this can also be 0012 -> 1110 and 1112 -> 2210 or even 2222 -> 3320 or even 3333 -> 4431)

Básicamente, cuando es mayor que dos: quite dos, no agregue nada en el dígito anterior, agregue uno al dígito anterior, luego agregue uno al dígito anterior.

En pseudocódigo:

if(a[n]>2):
    a[n] -= 2;
    a[n-2] += 1;
    a[n-3] += 1;

Implementación unaria:

Cada dígito (p 3. Ej. ) Se muestra como el número de xs (p xxx. Ej. ) Y luego se antepone con 0.

Por ejemplo, 1234se expresaría como 0x0xx0xxx0xxxx.

Esto deja 0sin cambios, como 101se expresaría por 0x00x.

Como inicialmente y finalmente, solo hay 0y 1, la conversión podría hacerse fácilmente por 1->0xy 0x->1.

Haga clic aquí para ver cada paso .

Monja permeable
fuente
1

JavaScript (ES6), 146126 bytes

r=n=>n&&n%2-r(n>>=1)-i(n)
i=n=>n&&r(n>>=1)-i(n)
g=(x,y,b=(x^y)&1)=>x|y&&b+2*g(b-x+y>>1,b-x-y>>1)
(x,y)=>g(r(x)+r(y),i(x)+i(y))

gconvierte un entero gaussiano (partes reales e imaginarias) en base i-1, mientras que ry iconvierte un i-1entero base en un entero gaussiano (partes reales e imaginarias respectivamente). Una vez que las conversiones están en su lugar, solo tengo que hacer la aritmética.

Editar: ahorró 20 bytes calculando las partes real e imaginaria por separado.

Neil
fuente
1

C ++ 416 bytes, más #include <vector>\n#include <algorithm>\n(otros 40)

using I=int;using v=std::vector<I>;void r(v&x){v r{rbegin(x),rend(x)};x=r;}v a(v L,v R){r(L);r(R);L.resize(std::max(L.size(),R.size()));for(int&r:R)L[&r-R.data()]+=r;while(1){L.resize(L.size()+3);auto it=find(rbegin(L),rend(L),2);if(it==rend(L))break;I i=-1+it.base()-begin(L);i&&L[i+1]&&L[i-1]/2?L[i+1]=L[i]=L[i-1]=0:(++L[i+2],++L[i+3],L[i]=0);}L.erase( std::find(rbegin(L),rend(L),1).base(),end(L));r(L);return L;}

o, con más espacios en blanco:

using I=int;
using v=std::vector<I>;

void r(v&x){v r{rbegin(x),rend(x)};x=r;}
v a(v L,v R) {
  r(L);r(R);
  L.resize(std::max(L.size(),R.size()));
  for(int&r:R)
    L[&r-R.data()]+=r;
  while(1) {
    L.resize(L.size()+3);
    auto it=find(rbegin(L), rend(L), 2);
    if(it==rend(L)) break;
    I i=-1+it.base()-begin(L);
    i&&L[i+1]&&L[i-1]/2?
      L[i+1]=L[i]=L[i-1]=0
    :
      (++L[i+2],++L[i+3],L[i]=0);
  }
  L.erase( std::find(rbegin(L),rend(L),1).base(), end(L));
  r(L);
  return L;
}

Apenas golfizado. Toma la entrada como un vector de entradas y devuelve un vector de entradas.

Ejemplo en vivo .

Yakk
fuente