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.