Vamos fazer uma localização aritmética!


22

Do artigo da Wikipedia :

A aritmética de localização (Latin arithmeticæ localis) é o sistema numérico binário aditivo (não-posicional), que John Napier explorou como técnica de computação em seu tratado Rabdology (1617), tanto simbolicamente quanto em uma grade semelhante a um tabuleiro de xadrez.

O que?

Os números de localização são uma maneira de escrever números usando as letras do alfabeto.

A notação binária ainda não havia sido padronizada, portanto Napier usou o que chamou de numerais de localização para representar números binários. O sistema de Napier usa notação de valor de sinal para representar números; usa letras sucessivas do alfabeto inglês para representar potências sucessivas de dois: a = 2 ^ 0 = 1, b = 2 ^ 1 = 2, c = 2 ^ 2 = 4, d = 2 ^ 3 = 8, e = 2 ^ 4 = 16 e assim por diante.

Um exemplo

ab = 1 + 2 = 3 na base 10

aabb = 1 + 1 + 2 + 2 = 6 na base 10

Observe que isso aabbpode ser reduzido bc, substituindo as duas instâncias de uma carta por uma mais alta.

Adição

Você apenas concatena os dois números e simplifica.

acd+ bde= acdbde= abcdde= acebe= abcf= 39na base 10

Subtração

Apenas remova todos os dígitos que aparecem igualmente nas duas partes da subtração. Pode ser necessário expandir (converter bpara aa)

abde- ad= be= 18 na base 10

Multiplicação

Isso é um pouco mais difícil.

Digamos que queremos multiplicar acd(13) por def(56). Primeiro você organiza acdverticalmente:

a
c
d

Então você adiciona defapós o primeiro a:

a def
c
d

Agora, c é 2 posições mais tarde no alfabeto que a, então adicionamos 2 posições no alfabeto defpara fazer fgh. Isso é adicionado à segunda linha.

a def
c fgh
d

Por fim, d é 1 posição mais tarde no alfabeto que c, portanto, adicionamos 1 posição no alfabeto a fghser criada ghi. Isso é adicionado à terceira linha.

a def
c fgh
d ghi

Então você toma a soma do direito: def+ fgh+ ghi= deffgghhi= deggghhi= deghhhi= deghii= = deghj(728)

Outro exemplo de multiplicação

Entrada:

bc * de

Primeiro:

b
c

Então

b ef
c 

Então

b ef
c fg

Observe que anotamos efna primeira linha. Isso ocorre porque bccomeça com be bé a segunda letra do alfabeto, então precisamos mudar deuma letra para que ela se torne ef.

Então

ef+fg

Saída:

eh

Divisão

Isso não faz parte desse desafio, porque pode ficar muito complexo.

Seu verdadeiro desafio

Seu programa ou função deve receber a entrada como uma string parecida com esta:

a + b

E você deve produzir:

ab

Naturalmente, o seu programa ou função deve suportar números de comprimento arbitrário (até a corda ou a entrada limite do seu idioma) com qualquer um dos operadores +, -ou *. Mais alguns exemplos:

Entrada:

ab + bd

Saída:

acd

Entrada:

d - ab

Saída:

ac

Entrada:

ab * cd

Saída:

cf

Notas:

  • A ordem das letras na saída não importa, mas você sempre pode assumir que a ordem das letras nos números da entrada será crescente (a antes de z).
  • Você pode receber entradas com uma nova linha à direita e produzir com uma nova linha à direita.
  • Você pode não ter a entrada como uma lista de ab, *e bdpara ab * bd.
  • O alfabeto inglês é usado ( abcdefghijklmnopqrstuvwxyz)
  • Sua saída deve ser simplificada ( aanão é permitido,b é necessário)
  • A entrada será simplificada ( b+ c, não aa+ bbou aa+ aaaa)
  • Você pode exigir um espaço antes e o operador ( +, -ou *), ou você pode exigir que haja nenhum.
  • Haverá apenas um operador por entrada.
  • Você pode assumir que a saída e a entrada nunca ultrapassarão 2 ^ 27-1 ( abcdefghijklmnopqrstuvwxyz)
  • Isso é , então a resposta mais curta em bytes vence!

2
d is 2 positions later in the alphabet than cisso é certo? não deveria ser 1? That is added to the second row.na mesma frase, não deveria third?
— Felipe Nardi Batista

1
@FelipeNardiBatista o alfabeto inglês é usado aqui, editou isso.
— programmer5000

@ programmer5000 ainda, bc*de==efghmas efghé 240não144
— Felipe Nardi Batista

1
bc*dedeve sereh
— Felipe Nardi Batista

@ Dadá, haverá apenas um operador por entrada.
— programmer5000

Respostas:


3

Geléia , 26 25 bytes

i@€Øað’2*S;ḟ.Ḣ
ḲÇ€VBṚTịØa

Usa os operadores do Jelly (em ×vez de *e em _vez de -) na string de entrada, conforme permitido pelo OP .

(Requer espaços ao redor dos operadores)

Experimente online! ou veja a suíte de testes

Quão?

i@€Øað’2*S;ḟ.Ḣ - Link 1, transform from input sub-string to value or operator: sub-string
i@€            - 1st index of, for €ach (or 0 if not found) [reversed @rguments] in:
   Øa          -      lowercase alphabet (i.e. a->1, b->2, ..., non-alpha->0)
     ð         - dyadic chain separation i.e. f(result above, substring):
      ’        - decrement (i.e a->0, b->1, ..., non-alpha->-1)
       2*      - 2 raised to that power
         S     - sum
          ;    - concatenate with the substring
           ḟ   - filter out:
            .  -     0.5 (for an operator substring the evaluated 0.5 is removed)
             Ḣ - head (i.e. the evaluation for a location, and the operator otherwise)

ḲÇ€VBṚTịØa - Main link: string                        e.g. 'ab × cd'
Ḳ          - split on spaces                               [['a','b'],['×'],['c','d']]
 Ç€        - last link (1) as a monadic function for €ach  [3,'×',12]
   V       - evaluate as Jelly code                        36
    B      - convert to binary                             [1,0,0,1,0,0]
     Ṛ     - reverse                                       [0,0,1,0,0,1]
      T    - truthy indexes                                [3,6]
       ị   - index into:
        Øa -     lowercase alphabet                        ['c','f'] (i.e. "cf", which is implicitly printed when run as a full program)

7

Mathematica, 168 bytes

FixedPoint[StringReplace[x_~~x_:>FromCharacterCode[c@x+1]],Table["a",ToExpression@StringReplace[#,x:LetterCharacter..:>ToString@Tr[2^((c=ToCharacterCode)@x-97)]]]<>""]&

Minha solução inicial (antes da edição da postagem para esclarecer que a saída deve ser simplificada) foi 64 bytes mais curtos:

Table["a",ToExpression@StringReplace[#,x:LetterCharacter..:>ToString@Tr[2^(ToCharacterCode@x-97)]]]<>""

Isso apenas modificou essa solução para funcionar. Provavelmente é mais curto para realmente usar os métodos descritos no desafio, mas eu queria colocar isso de qualquer maneira.

Explicação:

Substitui cada sequência de letras pelo número inteiro correspondente pelo código de caracteres aritmético, depois converte a sequência resultante em uma expressão (que simplifica automaticamente para um número inteiro), produz uma sequência de acaracteres de comprimento igual a esse número inteiro e, finalmente, substitui os caracteres idênticos adjacentes. caracteres com o próximo caractere codificam até que um ponto fixo seja alcançado.


2
Oh, não há 1 char embutido? Surpreendente!
— programmer5000

7

JavaScript (ES6), 136 134 133 bytes

Guardado 1 byte graças a Luke

s=>[...a='abcdefghijklmnopqrstuvwxyz'].filter((c,i)=>eval(s.replace(/\w+/g,s=>[...s].reduce((p,c)=>p|1<<a.search(c),0)))&1<<i).join``

Casos de teste


Bem feito! Você me venceu ...
— programmer5000

Isso converte em decimal e vice-versa? Parece que sim.
— programmer5000

1
@ programmer5000 Sim, de fato. Eu suspeito que muitas respostas serão. (Exceto, é claro, o Mathematica, que provavelmente possui um built-in. ^^)
— Arnauld

Parece que seu comentário estava sem um link. O que tem um built-in para isso?
— programmer5000

@ programmer5000 (Na verdade, faltava uma palavra.)
— Arnauld

5

Perl 5 , 95 bytes

94 bytes de código + -psinalizador.

s/\w/a x 2**(-97+ord$&)/ge;s/(.*)-\1|\+//;/\*/&&($_=$`x length$');1while s/(.)\1/chr 1+ord$1/e

Experimente online!

Três etapas aqui:
- s/\w/a x 2**(-97+ord$&)/ge;converte a entrada em uma sequência de aapenas.
- s/(.*)-\1|+//;/*/&&($_=$`x length$')executará o operador (que é muito simples em cadeias de a): +é a concatenação,- significa remover da primeira parte quantas ahouver na segunda parte e *duplicar a primeira parte quantas vezes houver ana segunda parte.
- 1while s/(.)\1/chr 1+ord$1/edobra as mesmas letras consecutivas na próxima letra do alfabeto.


A única resposta que não converte em decimal! Bom trabalho!
— programmer5000

1
@ programmer5000 Das 2 respostas, eu não chamaria isso de impressionante!
— Dada

5

05AB1E , 29 bytes

ð¡À¬U¦v0yvAyko+}}X.VbRvyiANèJ

Experimente online! ou como um conjunto de testes

Explicação

ð¡                             # split input on string
  À                            # rotate left
   ¬U¦                         # get the operator, store it in X and remove it from list
      v                        # for each side of the equation
       0                       # push 0 as an accumulator
        yv                     # for each letter in each side of the equation
          Ayk                  # get its index in the alphabet
             o                 # raise 2 to this power
              +                # add to the accumulator
               }}              # end loops
                 X.V           # apply the operator to the 2 numbers now on the stack
                    bR         # convert to binary and reverse
                      v        # for each binary digit
                       yi      # if it is true
                         ANè   # get the letter at that index in the alphabet
                            J  # join stack to a single string

5

C & x86 asm, 340 bytes

Compilar com -O0

#define G getchar()
g(){int c,a=0;for(;islower(c=G);)a+=1<<(c-97);return a;}
main(){short o[]={[43]=0x4403,[45]=0x442b,[42]=0x6cf7};
mprotect((long)&&l&~4095,4096,7);
for(;;){int c,b=0,a=g();*(short*)&&l=o[G];G;g();asm("xchg %%eax,%0":"+m"(a));
l:asm("addl %1,%%eax":"=a"(c):"m"(a));
for(;a=c>>b;b++)if(a&=1)putchar(97+b);putchar(10);}}

Explicação

Desde C não tem eval() , usei uma tabela de instruções x86 em seu lugar. Eu tive que escolher instruções com o mesmo comprimento (ou preenchidas com nops) e quais esperavam src e destino dos mesmos tipos. Um incômodo particular foi o fato de o MUL apenas gravar nos registros, e os opcodes MUL de 1 byte podem gravar no EAX. Além disso, parecia não haver nenhuma instrução SUB de gravação de registro que subtraísse da memória, e não o contrário, daí o XCHG.

editar

Como foi solicitado nos comentários, uma abordagem mais tradicional seria assim:

#define G getchar()
#define return r
#define int i
g(){i c,a=0;for(;islower(c=G);)a+=1<<(c-97);r a;}
a(i x,i y){r x+y;}s(i x,i y){r x-y;}m(i x,i y){r x*y;}
main(){i(*o[])(i,i)={[43]=a,[45]=s,[42]=m};
for(;;){i c,b,a=g();b=G;G;g();c=o[b](a,g());
for(b=0;a=c>>b;b++)if(a&=1)putchar(97+b);putchar(10);}}

Na verdade, é um pouco mais curto, com 301 caracteres, por alguns motivos: 1. Como é preciso haver muitas funções, a sobrecarga de cada uma pode ser cortada com algumas regras do pré-processador. 2. O linux moderno protege da execução na pilha; portanto, o mprotect () chama para desativar esses 34 bytes sacrificados. 3. A chamada XCHG é muito subótima, custando outros 30 bytes. Se não fosse por essas coisas, o combo x86 venceria em cerca de 10 a 20 bytes.

Também cortou 2 bytes de ambos melhorando a chamada islower () em g.


Não sei como comparar com uma abordagem mais clássica em termos de tamanho de código, mas realmente gosto da sua solução. +1
— Arnauld

5

GNU sed + coreutils, 329 bytes

Sim, eu não tenho ideia do que aconteceu comigo, mas pelo menos eu sei que os scripts estão um pouco melhores agora. Observe que esta solução requer a eextensão do GNU sed , que executa um comando shell.

/\+/{s/\+//
b S}
/-/{:E
/a+-a+/{s/(a*)(a*)-\2/\1/
b S}
s/.*/echo &|tr b-z- A-Y-/
e
s/([A-Z])/\L\1\1/g
b E}
/\*/{h
:M
/^\*/{x
s/[^\n]*//
s/\n//g
b S}
s/(.).*\*(.*)/echo \2|tr a-z \1-za-z/
e
H
g
s/.(.*)/\1/
h
s/\n.*//
b M}
:S
s/^.*$/echo &|grep -o .|sort|tr -d '\n'/
e
:L
s/(.)\1/\u\1/g
/^[a-z]*$/ q
s/.*/echo &|tr A-Z b-za/;e
b L

Presumo que não haverá espaços ao redor dos operadores. Do meu terminal:

$ sed -rf golf.sed <<< a+b
ab
$ sed -rf golf.sed <<< ab+bd
acd
$ sed -rf golf.sed <<< abc+b
ad
$ sed -rf golf.sed <<< d-ab
ca
$ sed -rf golf.sed <<< ab*cd
cf
$ sed -rf golf.sed <<< bc*de
eh
$ sed -rf golf.sed <<< acd*def
deghj

E, para aqueles mais sãos do que eu: a versão comentada!

#!/bin/sed -rf

/\+/ {
    s/\+//
    b simplify
}

/-/ {
    # expand pattern space; everything will now be 'a's
    :E
    /a+-a+/{
        # Remove doubled 'a's on either side of the dash. For example,
        # for input d-ab, space is now 'aaaa-aaa'; substitute this to 'a'
        s/(a*)(a*)-\2/\1/
        b simplify
    }
    # shift letters that aren't 'a' down and double them
    s/.*/echo &|tr b-z- A-Y-/;e
    s/([A-Z])/\L\1\1/g
    b E
}

/\*/ {
    # Hold space: line 1 is pattern, other lines are output
    h
    :M

    # if space starts with *, we've eaten entire arg0; sum and simplify
    /^\*/ {
        x
        s/[^\n]*//      # remove first line, which is our pattern
        s/\n//g         # remove newlines to add results together
        b simplify
    }

    # convert pattern into shifting command
    s/(.).*\*(.*)/echo \2|tr a-z \1-za-z/

    # execute it, append result to hold space
    e
    H

    # restore pattern, with leading char and all output lines removed
    g
    s/.(.*)/\1/
    h
    s/\n.*//

    b M
}

:simplify
# reorder all letters so all 'a's are before all 'b's are before all 'c's
# are before ... etc    
# See /programming/2373874
s/^.*$/echo &|grep -o .|sort|tr -d '\n'/
e

:L
# Replace repeated characters with themselves upper-cased, then translate
# upper-cased characters to what they should be.
s/(.)\1/\u\1/g
/^[a-z]*$/ q
s/.*/echo &|tr A-Z b-za/;e
b L

+1 para o código sed e seja bem-vindo ao PPCG! A convenção aqui quando não se resolve no GNU sed puro (ou em qualquer outra linguagem pura), é adicionar ao título os comandos do sistema utilizados, como "GNU sed + coreutils" por exemplo, mesmo se você mencionar a chamada de um comando shell na descrição . Isso é feito para diferenciar, especialmente em desafios com quadros de líderes, das respostas puras do GNU sed.
— seshoumara

Além disso, exceto pelo sinalizador 'f' necessário sempre, qualquer outro sinalizador deve ser contado como 1 byte. Portanto, sua pontuação é 329. Você pode mencionar isso na descrição. E, para concluir, você pode adicionar um link a um intérprete online, como o TIO .
— precisa saber é o seguinte

Para não ser tudo conversa e nenhuma ação, aqui está um 43 bytes mais curto! versão do seu código (286 bytes, incluindo -r), que encontrei ao jogar os comandos. Tenho certeza que pode ser ainda mais curto.
— precisa saber é o seguinte

Ah, tudo bem, é bom saber! Além disso, bom golfe! Qual versão do sed você está usando? Seu trabalha na TIO, mas em GNU sed 4,4 I apenas sesed: file golf.sed line 24: ":" lacks a label
— charliegreen

O rótulo sem nome é um bug bem conhecido no GNU sed, que foi corrigido na versão 4.3. Mas no PPCG, você pode escrever programas para qualquer versão e versão sed, usando bugs como recursos, se isso ajudar no golfe. As diferenças entre as versões são pequenas demais para serem mencionadas (4.2 vs 4.4), mas a variante (POSIX sed padrão versus GNU sed estendido) precisa ser especificada no título, com a menção de programas do sistema chamados, se houver.
— precisa saber é o seguinte

4

PHP, 168

Saída Crescente com o uso de eval

[$a,$o,$b]=explode(" ",$argn);function d($s){for(;$i<strlen($s);)$n+=2**(ord($s[$i++])-97);return$n;}for(eval("\$k=d($a)$o d($b);");$i<26;)echo$k&2**$i++?chr(96+$i):"";

PHP, 185 bytes

Saída Crescente

[$a,$o,$b]=explode(" ",$argn);function d($s){for(;$i<strlen($s);)$n+=2**(ord($s[$i++])-97);return$n;}for(;$i<26;)echo(bc.[mul,add,0,sub][ord($o)-42])(d($a),d($b))&2**$i++?chr(96+$i):"";

Versão Online

Expandido

[$a,$o,$b]=explode(" ",$argn); # part the input into variables
function d($s){ # make decimal value
    for(;$i<strlen($s);)$n+=2**(ord($s[$i++])-97);
    return$n;
}
for(;$i<26;)
echo(bc.[mul,add,0,sub][ord($o)-42])(d($a),d($b))&2**$i++?chr(96+$i):""; # Bitwise Compare and Output

PHP, 201 bytes

Saída Decrescente

[$a,$o,$b]=explode(" ",$argn);function d($s){for(;$i<strlen($s);)$n+=2**(ord($s[$i++])-97);return$n;}for($r=(bc.[mul,add,0,sub][ord($o)-42])(d($a),d($b));$r;$r-=2**$l)$t.=chr(97+$l=log($r,2)^0);echo$t;

Versão Online

Expandido

[$a,$o,$b]=explode(" ",$argn); # part the input into variables
function d($s){ # make decimal value
    for(;$i<strlen($s);)$n+=2**(ord($s[$i++])-97);
    return$n;
}
for(
$r=(bc.[mul,add,0,sub][ord($o)-42])(d($a),d($b)) # result of the operation
;$r;
$r-=2**$l) # subtract the letter value 
$t.=chr(97+$l=log($r,2)^0); # find greatest letter
echo$t; # Output

4

Python 3 , 176 167 bytes

i=lambda a:str(sum(1<<ord(i)-97for i in a))
def f(a):
 a,b,c=a.split();m=eval(i(a)+b+i(c));r=''
 while m:
  t=0
  while m>=2**t*2:t+=1
  r+=chr(97+t);m-=2**t
 return r

Experimente online!

  • economizou 9 bytes: Graças a tutleman

1
A menos que eu esteja enganado, você pode cortar dois bytes substituindo m>=2**(t+1)por m>=2**t*2e cinco bytes substituindo a=a.split();m=eval(i(a[0])+a[1]+i(a[2]))por algo parecido b,c,d=a.split();m=eval(i(b)+c+i(d)).
— Tutleman

1
Ah, e mais dois bytes substituindo 2**(ord(i)-97)por 1<<ord(i)-97.
— Tutleman

1
Estou impressionado com a legibilidade dessa solução em comparação com outras soluções.
— precisa saber é o seguinte

Obrigado :). Mas acho que é também por causa do python ser a linguagem usada. O recuo aumenta a contagem de bytes, por mais legível. ;)
— officialaimm

2

PHP, 130

for($d=a;$e=$argn[$i++];)$e!=' '?$d!=b?$$d+=1<<ord($e)-97:$b=$e:++$d;eval("for(;\$j++<27;)echo($a$b$c>>\$j-1)&1?chr(96+\$j):'';");

versão expandida:

for($d=a;$e=$argn[$i++];)       // for each char in the input
  $e!=' '?                      //   if space
    $d!=b?                      //     if not the operation
      $$d+=1<<ord($e)-97:       //       add 2^(char - 'a')
      $b=$e:                    //     else save operation
    ++$d;                       //   else increase "pointer"
eval("for(;\$j++<27;)           // for each bit in the output
        echo($a$b$c>>\$j-1)&1?  //   calulate the result and check the bit
          chr(96+\$j):          //     output corrosponding char
          '';                   //     output nothing
     ");

correr com php -R <code> .


1

AWK, 201 bytes

BEGIN{RS="(.)"}n=index(V="abcdefghijklmnopqrstuvwxyz",RT){s+=2^--n}index("+-*",RT){a=s RT
s=0}END{RS="\n"
"(awk '$0="a s"'<<<1)"|getline v
for(j=26;j--;)if((s=v-2^j)>=0){v=s;c=substr(V,j+1,1)c}print c}

"(awk '$0="a s"'<<<1)"|getline vé a melhor maneira que eu poderia chegar a fazer um evaluateno AWK. Eu posso estar "trapaceando" um pouco para chamar isso apenas AWK, já que estou executando um comando, mas pelo menos o comando também é AWK:)

Tenho certeza de que estou perdendo uma maneira de reduzir a contagem de bytes, mas com certeza não consigo vê-lo.

O uso é bastante padrão, por exemplo, insira o código FILEe faça:

awk -f FILE <<< "bc + ab"

Observe que os espaços não são necessários e qualquer caractere não op / não [az] será ignorado silenciosamente. Pode ser estendido para trabalhar com números maiores que "abcdefghijklmnopqrstuvwxyz", alterando o loop. Para fazer a divisão, basta adicionar o /caractere à operação string :). Além disso, imprimirá uma linha em branco se o result <= 0.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.