Zróbmy trochę arytmetyki lokalizacji!

22

Z artykułu w Wikipedii :

Arytmetyka lokalizacji (arytmetyka łacińska localis) to addytywne (nie-pozycyjne) układy liczb binarnych, które John Napier badał jako technikę obliczeniową w swoim traktacie Rabdology (1617), zarówno symbolicznie, jak i na szachownicy.

Co?

Cyfry lokalizacji to sposób wpisywania liczb za pomocą liter alfabetu.

Notacja binarna nie została jeszcze ustandaryzowana, więc Napier użył czegoś, co nazwał liczbami lokalizacji, do przedstawienia liczb binarnych. System Napiera używa notacji znak-wartość do reprezentowania liczb; używa kolejnych liter alfabetu angielskiego do reprezentowania kolejnych potęg dwóch: a = 2 ^ 0 = 1, b = 2 ^ 1 = 2, c = 2 ^ 2 = 4, d = 2 ^ 3 = 8, e = 2 ^ 4 = 16 i tak dalej.

Przykład

ab = 1 + 2 = 3 w podstawie 10

aabb = 1 + 1 + 2 + 2 = 6 w podstawie 10

Zauważ, że aabbmożna to skrócić bc, zastępując dowolne 2 wystąpienia litery wyższym.

Dodanie

Po prostu połącz dwie liczby i uprość.

acd+ bde= acdbde= abcdde= acebe= abcf= 39w podstawie 10

Odejmowanie

Wystarczy usunąć wszystkie cyfry pojawiające się jednakowo w obu częściach odejmowania. Konieczne może być rozszerzenie (konwersja bna aa)

abde- ad= be= 18 w podstawie 10

Mnożenie

To jest trochę trudniejsze.

Powiedzmy, że chcemy pomnożyć acd(13) przez def(56). Najpierw ustaw acdpionowo:

a
c
d

Następnie dodajesz defpo pierwszym a:

a def
c
d

Teraz c ma 2 pozycje później w alfabecie niż a, więc dodajemy 2 pozycje w alfabecie, defaby zrobić fgh. To jest dodawane do drugiego rzędu.

a def
c fgh
d

Wreszcie d jest o 1 pozycję później w alfabecie niż c, więc dodajemy 1 pozycję w alfabecie, fghaby zrobić ghi. To jest dodawane do trzeciego rzędu.

a def
c fgh
d ghi

Następnie bierzesz sumę prawa: def+ fgh+ ghi= deffgghhi= deggghhi= deghhhi= deghii= deghj(728)

Kolejny przykład mnożenia

Wkład:

bc * de

Pierwszy:

b
c

Następnie

b ef
c 

Następnie

b ef
c fg

Zauważ, że zapisaliśmy efw pierwszym wierszu. To dlatego, że bczaczyna się od b, i bjest drugą literą w alfabecie, więc musimy przesunąć deo 1 literę, aby stała się ef.

Następnie

ef+fg

Wydajność:

eh

Podział

To nie jest część tego wyzwania, ponieważ może stać się bardzo złożone.

Twoje rzeczywiste wyzwanie

Twój program lub funkcja musi przyjmować dane wejściowe jako ciąg znaków, który wygląda następująco:

a + b

I musisz wydać:

ab

Oczywiście, program lub funkcja musi obsługiwać numery dowolnej długości (do łańcucha lub wejściowego granicy języka) z żadnym z operatorów +, -lub *. Kilka innych przykładów:

Wkład:

ab + bd

Wydajność:

acd

Wkład:

d - ab

Wydajność:

ac

Wkład:

ab * cd

Wydajność:

cf

Uwagi:

  • Kolejność liter w danych wyjściowych nie ma znaczenia, ale zawsze można założyć, że kolejność liter w liczbach na wejściu będzie rosła (a przed z).
  • Możesz pobierać dane wejściowe z końcowym znakiem nowej linii i dane wyjściowe z końcowym znakiem nowej linii.
  • Być może nie wziąć wkład w postaci listy ab, *a bdza ab * bd.
  • Używany jest alfabet angielski ( abcdefghijklmnopqrstuvwxyz)
  • Twój wynik musi być uproszczony ( aanie jest dozwolony, bjest wymagany)
  • Wprowadzanie danych zostanie uproszczone ( b+ c, nie aa+ bblub aa+ aaaa)
  • Może wymagać przestrzeń przed i operatora ( +, -lub *), lub może wymagać, aby istniała żadna.
  • Będzie tylko jeden operator na dane wejściowe.
  • Możesz założyć, że dane wyjściowe i wejściowe nigdy nie przekroczą 2 ^ 27-1 ( abcdefghijklmnopqrstuvwxyz)
  • To jest , więc wygrywa najkrótsza odpowiedź w bajtach!
programmer5000
źródło
2
d is 2 positions later in the alphabet than cczy to jest wright? nie powinno być 1? That is added to the second row.w tym samym zdaniu, prawda third?
Felipe Nardi Batista
1
@FelipeNardiBatista używany jest tutaj alfabet angielski, zredagowany.
programator5000
@ programmer5000 nadal, bc*de==efghale nie efghjest240144
Felipe Nardi Batista
1
bc*depowinno byćeh
Felipe Nardi Batista
@Dada będzie tylko jeden operator na wejście.
programator5000

Odpowiedzi:

3

Galaretka , 26 25 bajtów

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

Używa operatorów Jelly ( ×zamiast *i _raczej niż -) w ciągu wejściowym, na co pozwala OP .

(Wymaga odstępów wokół operatorów)

Wypróbuj online! lub zobacz pakiet testowy

W jaki sposób?

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)
Jonathan Allan
źródło
7

Mathematica, 168 bajtów

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

Moje początkowe rozwiązanie (przed edycją postu w celu wyjaśnienia, że ​​dane wyjściowe muszą zostać uproszczone) było 64krótsze bajtów:

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

To właśnie zmodyfikowało to rozwiązanie do pracy. Prawdopodobnie krócej jest użyć metod opisanych w wyzwaniu, ale i tak chciałem to podnieść.

Wyjaśnienie:

Zastępuje każdą sekwencję liter odpowiadającą liczbą całkowitą arytmetyką kodu znakowego, a następnie konwertuje wynikowy ciąg znaków na wyrażenie (które automatycznie upraszcza się na liczbę całkowitą), a następnie tworzy ciąg aznaków o długości równej tej liczbie całkowitej, a na koniec zastępuje sąsiednie identyczne znaki z kodem następnego znaku aż do osiągnięcia stałego punktu.

ngenisis
źródło
2
Och, nie ma wbudowanego 1-char? Zaskakujące!
programista
7

JavaScript (ES6), 136 134 133 bajtów

Zaoszczędził 1 bajt dzięki Luke'owi

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``

Przypadki testowe

Arnauld
źródło
Ładnie wykonane! Pobiłeś mnie do tego ...
programista
Czy to przelicza na dziesiętne iz powrotem? Wygląda na to, że tak.
programista
1
@ programmer5000 Tak, rzeczywiście. Podejrzewam, że wiele odpowiedzi tak. (Z wyjątkiem oczywiście Mathematica, która prawdopodobnie ma wbudowaną funkcję. ^^)
Arnauld
Wygląda na to, że w Twoim komentarzu brakowało linku. Co ma wbudowane zdjęcie?
programista
@ programmer5000 (W rzeczywistości brakowało słowa.)
Arnauld
5

Perl 5 , 95 bajtów

94 bajty kodu + -pflaga.

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

Wypróbuj online!

Tutaj trzy kroki:
- s/\w/a x 2**(-97+ord$&)/ge;przekształca dane wejściowe w ciąg atylko.
- s/(.*)-\1|+//;/*/&&($_=$`x length$')wykona operator (który jest bardzo prosty na ciągach znaków a): +jest konkatenacją, -oznacza usunięcie z pierwszej części tyle, aile jest w drugiej części, i *oznacza duplikowanie pierwszej części tyle razy, ile jest aw drugiej część.
- 1while s/(.)\1/chr 1+ord$1/eskłada kolejne te same litery do następnej litery w alfabecie.

Dada
źródło
Jedyna odpowiedź, która nie jest konwertowana na dziesiętną! Dobra robota!
programista
1
@ programmer5000 Z 2 odpowiedzi nie nazwałbym tego tak imponującym!
Dada,
5

05AB1E , 29 bajtów

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

Wypróbuj online! lub jako pakiet testowy

Wyjaśnienie

ð¡                             # 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
Emigna
źródło
5

C i x86 asm, 340 bajtów

Skompiluj z -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);}}

Wyjaśnienie

Ponieważ C nie ma eval(), zamiast tego użyłem tabeli instrukcji x86. Musiałem wybrać instrukcje, które były tej samej długości (lub były wypełnione nops) i które oczekiwały src i miejsca docelowego tego samego typu. Szczególnie denerwujące było to, że MUL może zapisywać tylko do rejestrów, a 1-bajtowe kody MUL mogą zapisywać tylko do EAX. Dodatkowo wydawało się, że nie ma instrukcji SUB zapisującej rejestry, która odejmowałaby się z pamięci zamiast na odwrót, stąd XCHG.

edytować

Ponieważ zostało to zadane w komentarzach, bardziej tradycyjna ocena wyglądałaby następująco:

#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);}}

Jest tak naprawdę nieco krótszy, przy 301 znakach, z kilku powodów: 1. Ponieważ potrzebnych jest wiele funkcji, narzut na każdą z nich można pokroić za pomocą pewnych reguł preprocesora. 2. Nowoczesny system Linux chroni przed wykonaniem na stosie, dlatego wywołanie mprotect () wyłącza poświęcone 34 bajty. 3. Wywołanie XCHG jest bardzo nieoptymalne i kosztuje kolejne 30 bajtów. Gdyby nie te rzeczy, kombinacja x86 wygrałaby o około 10-20 bajtów.

Również pocięliśmy 2 bajty z obu poprzez ulepszenie wywołania islower () wg.

Dave
źródło
Naprawdę nie potrafię powiedzieć, jak by to wyglądało w porównaniu z bardziej klasycznym podejściem pod względem wielkości kodu, ale naprawdę podoba mi się twoje rozwiązanie. +1
Arnauld
5

GNU sed + coreutils, 329 bajtów

Tak, nie mam pojęcia, co we mnie weszło, ale przynajmniej wiem, że teraz skrypt jest trochę lepszy. Zauważ, że to rozwiązanie wymaga erozszerzenia GNU sed , które uruchamia polecenie powłoki.

/\+/{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

Zakładam, że wokół operatorów nie będzie przestrzeni. Z mojego terminala:

$ 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

A dla tych rozsądniejszych ode mnie: skomentowana wersja!

#!/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
charliegreen
źródło
+1 za kod sed i witamy w PPCG! Konwencja tutaj, gdy nie jest rozwiązywana w czystym GNU sed (lub w jakimkolwiek innym czystym języku), polega na dodaniu do tytułu używanych poleceń systemowych, takich jak na przykład „GNU sed + coreutils”, nawet jeśli wspominasz o wywołaniu polecenia powłoki w opisie . Odbywa się to w celu odróżnienia, szczególnie w wyzwaniach z tabelami liderów, od czystych odpowiedzi GNU.
seshoumara,
Ponadto, z wyjątkiem flagi „f” potrzebnej za każdym razem, każda inna flaga musi być liczona jako 1 bajt. Więc twój wynik to 329. Możesz wspomnieć o tym w opisie. Aby zakończyć, możesz pomyśleć o dodaniu linku do internetowego tłumacza ustnego, takiego jak TIO .
seshoumara,
Aby nie rozmawiać i nie podejmować żadnych działań, oto 43 bajty krótsze! wersja twojego kodu (286 bajtów, w tym -r), którą znalazłem, grając w golfa poleceń. Jestem pewien, że może być jeszcze krótszy.
seshoumara,
Ach, dobrze, dobrze wiedzieć! Miłego golfa! Jakiej wersji sed używasz? Twój działa w TIO, ale w GNU sed 4.4 właśnie dostajęsed: file golf.sed line 24: ":" lacks a label
charliegreen 13.04.17
Bezimienna etykieta jest dobrze znanym błędem w GNU sed, który został naprawiony w wersji 4.3. Ale w PPCG możesz pisać programy dla każdego wariantu i wersji sed, używając błędów jako funkcji, jeśli pomaga w grze w golfa. Różnice między wersjami są zbyt małe, aby je wymienić (4.2 vs 4.4), ale wariant (standardowy POSIX sed vs rozszerzony GNU sed) musi zostać określony w tytule, z podaniem ewentualnych programów systemowych.
seshoumara,
4

PHP, 168

Wyjście Rosnąco za pomocą 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 bajtów

Wyjście Rosnąco

[$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):"";

Wersja online

Rozszerzony

[$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 bajtów

Wyjście Decending

[$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;

Wersja online

Rozszerzony

[$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
Jörg Hülsermann
źródło
4

Python 3 , 176 167 bajtów

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

Wypróbuj online!

Officialaimm
źródło
1
O ile się nie mylę, możesz ogolić dwa bajty, zastępując m>=2**(t+1)je m>=2**t*2, i pięć bajtów, zastępując a=a.split();m=eval(i(a[0])+a[1]+i(a[2]))coś podobnego b,c,d=a.split();m=eval(i(b)+c+i(d)).
Tutleman
1
Aha, i jeszcze dwa bajty, zastępując 2**(ord(i)-97)z 1<<ord(i)-97.
Tutleman
1
Dziwi mnie, jak czytelne jest to rozwiązanie w porównaniu do innych rozwiązań.
Ole Tange
Dziękuję Ci :). Ale myślę, że dzieje się tak również dlatego, że python jest językiem używanym. Wcięcie powoduje zwiększenie liczby bajtów, jakkolwiek czytelne. ;)
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):'';");

wersja rozszerzona:

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
     ");

biegać z php -R <code>.

Christoph
źródło
1

AWK, 201 bajtów

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 vJest to najlepszy sposób mogłem wymyślić, aby zrobić evaluatein AWK. Być może trochę oszukuję, żeby to nazwać AWK, ponieważ wykonuję polecenie, ale przynajmniej polecenie jest równieżAWK :)

Jestem pewien, że brakuje mi jakiegoś sposobu na zmniejszenie liczby bajtów, ale na pewno tego nie widzę.

Użycie jest dość standardowe, np. Wstaw kod FILEi wykonaj:

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

Zauważ, że spacje nie są wymagane, a wszelkie znaki spoza / non [az] zostaną po cichu zignorowane. Można go rozszerzyć do pracy z liczbami większymi niż „abcdefghijklmnopqrstuvwxyz” poprzez zmianę pętli. Aby wykonać podział, wystarczy dodać /znak do ciągu operacji :). Również wydrukuje pustą linię, jeśli result <= 0.

Robert Benson
źródło