Implementar adição de sequência verdadeira

29

Muitos idiomas permitem que as strings sejam "adicionadas" +. No entanto, isso é realmente concatenação, uma verdadeira adição seguiria os axiomas do grupo:

  • Está fechado (a adição de duas cordas é sempre uma string)

  • É associativo ( (a + b) + c = a + (b + c) )

  • Existe uma identidade ( e: a + e = a )

  • Todo elemento tem uma inversa ( :a: ∃b: a + b = e )

(concatenação viola o 4º axioma do grupo)

Portanto, minha tarefa para você é implementar uma adição verdadeira de cadeias, que é uma função que pega duas seqüências de bytes representando cadeias e retorna uma terceira, de modo que sua função satisfaça todos os axiomas do grupo nas seqüências de bytes.

Ele deve funcionar em todas as seqüências de bytes que representam seqüências de caracteres, incluindo aquelas com bytes nulos.

Isso é então as respostas serão pontuadas em bytes, com menos bytes sendo melhores.

Assistente de Trigo
fonte

Respostas:

5

Python 3 , 177 170 163 130 bytes

lambda a,b:s(d(a)^d(b))
def s(n,x=0,s=''):
 while n:n-=1;s+=chr(n%256);n>>=8
 return s
def d(n,c=0):
 while s(c)!=n:c+=1
 return c

Experimente online!

-14 bytes graças a notjagan

-33 bytes graças a Leaky Nun (e endianness comutado)

Não tenho negócios tentando jogar golfe em Python, mas não queria usar Lua, pois esse método precisa de números inteiros exatos grandes para funcionar com picadas de comprimento razoável. (Nota: o algoritmo ainda é muito lento ao aumentar o comprimento da string.) Isso é apenas para fornecer uma resposta;)

Cada sequência é auto-inversa e a sequência vazia é a identidade. Isso simplesmente executa xor sob uma simples bijeção entre cadeias e números inteiros não negativos. sé uma função auxiliar que calcula a bijeção (apenas de sentido único) e dé a inversa.

Versão não lenta (148 bytes, cortesia de Leaky Nun):

lambda a,b:s(d(a)^d(b))
def s(n,x=0,s=''):
 while n:n-=1;s=chr(n%256)+s;n>>=8
 return s
def d(n,c=0):
 while n:c=c*256+ord(n[0])+1;n=n[1:]
 return c

Experimente online!

Vou sequestrar isso também para uma cartilha de teoria de grupos.

Qualquer inversa à direita é uma inversa à esquerda: inv (a) + a = (inv (a) + a) + e = (inv (a) + a) + (inv (a) + inv (inv (a))) = Inv (a) + (a + inv (a)) + inv (inv (a)) = (inv (a) + e) ​​+ inv (inv (a)) = inv (a) + inv (a (inv)) ) = e

Isso também significa que a é um inverso de inv (a) .

Qualquer identidade certa é uma identidade esquerda: e + a = (a + inv (a)) + a = a + (inv (a) + a) = a

A identidade é única, dada outra identidade f : e = e + f = f

Se a + x = a então x = e : x = e + x = (inv (a) + a) + x = inv (a) + (a + x) = inv (a) + a = e

Inversos são únicos, se a + x = e então: x = e + x = (inv (a) + a) + x = inv (a) + (a + x) = inv (a) + e = inv (a )

Seguir as provas deve facilitar bastante a construção de contra-exemplos para soluções propostas que não satisfazem essas proposições.

Aqui está um algoritmo mais natural que eu implementei (mas não joguei golfe) em Lua . Talvez isso dê uma ideia a alguém.

function string_to_list(s)
  local list_val = {}
  local pow2 = 2 ^ (math.log(#s, 2) // 1) -- // 1 to round down
  local offset = 0
  list_val.p = pow2
  while pow2 > 0 do
    list_val[pow2] = 0
    if pow2 & #s ~= 0 then
      for k = 1, pow2 do
        list_val[pow2] = 256 * list_val[pow2] + s:byte(offset + k)
      end
      list_val[pow2] = list_val[pow2] + 1
      offset = offset + pow2
    end
    pow2 = pow2 // 2
  end
  return list_val
end

function list_to_string(list_val)
  local s = ""
  local pow2 = list_val.p
  while pow2 > 0 do
    if list_val[pow2] then
      local x = list_val[pow2] % (256 ^ pow2 + 1)
      if x ~= 0 then
        x = x - 1
        local part = ""
        for k = 1, pow2 do
          part = string.char(x % 256) .. part
          x = x // 256
        end
        s = s .. part
      end
    end
    pow2 = pow2 // 2
  end
  return s
end

function list_add(list_val1, list_val2)
  local result = {}
  local pow2 = math.max(list_val1.p, list_val2.p)
  result.p = pow2
  while pow2 > 0 do
    result[pow2] = (list_val1[pow2] or 0) + (list_val2[pow2] or 0)
    pow2 = pow2 // 2
  end
  return result
end

function string_add(s1, s2)
  return list_to_string(list_add(string_to_list(s1), string_to_list(s2)))
end

A idéia é basicamente dividir a string com base na potência de dois componentes de seu comprimento e tratá-los como campos com um componente ausente representando zero e cada componente não ausente representando números de 1 a 256 ^ n, então 256 ^ n + 1 total de valores. Em seguida, essas representações podem ser adicionadas módulo módulo 256 ^ n + 1.

Nota: Esta implementação de Lua terá problemas de estouro numérico para cadeias de tamanhos maiores que 7. Mas o conjunto de cadeias de comprimento 7 ou menos é fechado nesta adição.

Experimente online!

tehtmi
fonte
Curiosidade: como todo elemento é inverso, esse grupo também é abeliano.
Assistente de trigo
4

Gelatina , 8 bytes

‘ḅ⁹^/ḃ⁹’

Isto utiliza um mapeamento bijective & Phi de matrizes de bytes para os inteiros não negativos, XORs o resultado da aplicação φ para duas cadeias de entrada, em seguida, aplica-se φ -1 para o resultado.

A matriz vazia é o elemento neutro e todas as matrizes de bytes são inversas.

Experimente online!

Como funciona

‘ḅ⁹^/ḃ⁹’  Main link. Argument: [A, B] (pair of byte arrays)

‘         Increment all integers in A and B.
 ḅ⁹       Convert from base 256 to integer.
   ^/     XOR the resulting integers.
     ḃ⁹   Convert from integer to bijective base 256.
       ’  Subtract 1.
Dennis
fonte
Eu estava pensando que esolangs teve builtins de conversão de base bijective ...
Neil
Não deveria ser a base 257?
Titus
@Titus Não, os dígitos da base bijetiva 256 variam de 1 a 256 (inclusive).
Dennis
Então, ḅ⁹é da base bijetiva 256 para o número inteiro? O que A+Adá? chr(-1)?
Titus
@Titus O processo de conversão de base para número inteiro é idêntico para bases bijetivas e "normais". [65] + [65]vai render [].
Dennis
3

Python 2 , 114 bytes

lambda a,b:s(d(a)^d(b))
d=lambda s:s and d(s[1:])*256+ord(s[0])+1or 0
s=lambda d:d and chr(~-d%256)+s(~-d/256)or''

Experimente online! Trabalhos de XOR nas seqüências interpretadas como base bijetiva little-endiana 256.

Neil
fonte
d=lambda s:s>''and-~ord(s[0])+d(s[1:])*256salva três bytes; s=lambda d:d*'?'and chr(~-d%256)+s(~-d/256)salva mais um.
Lynn
@ Lynn Será que o segundo vai funcionar para d grande?
214 Neil
Como isso funciona se as seqüências não tiverem o mesmo comprimento?
Assistente de trigo
@WheatWizard O comprimento das strings é irrelevante. Há um mapeamento bijetivo do conjunto de strings para o conjunto de números inteiros. Os valores inteiros são XORed e o mapeamento invertido.
Neil
@ Neil Ok obrigado, vejo agora.
Assistente de trigo
1

Python 2 , 197 bytes

def n(s):
 n=s and ord(s[0])+1 or 0
 for c in s[1:]:n=n*256+ord(c)
 return(-1)**n*n/2
def f(l,r,s=""):
 i=n(l)+n(r)
 i=abs(i*2+(i<=0))
 while i>257:s=chr(i%256)+s;i/=256
 return["",chr(i-1)+s][i>0]

Experimente online!

Transforma a string em um número (reduzindo por código), nega se ímpar e depois reduz pela metade. Não é tão divertido quanto o outro, mas mais rápido: P

Somente ASCII
fonte
193 bytes
officialaimm
11
nnão é injetivo, o que causa problemas. Por exemplo, n("\x00\x00")==n("\xff")para que isso falhe:print(f("\x00\x00","") == "\x00\x00")
tehtmi
: | oh no que vai ser tão caro para correção
ASCII-only
1 or=>1or
Zacharý