Zrównoważony konwerter trójskładnikowy

32

Kredyty za pomysł na wyzwanie przejdź do @AndrewPiliser. Jego oryginalna propozycja w piaskownicy została porzucona, a ponieważ nie był tu aktywny od kilku miesięcy, podjąłem wyzwanie.

Zrównoważony trójskładnikowy jest niestandardowym systemem liczbowym. To jest jak trójkątny, na które cyfry wzrost wartości o współczynnik 3, jak pójdziesz dalej w lewo - tak100jest9i1001ma 28 lat.

Jednak zamiast wartości 0, 1 i 2, cyfry mają wartości -1, 0 i 1 . (Nadal możesz użyć tego do wyrażenia dowolnej liczby całkowitej).

W przypadku tego wyzwania znaczenie cyfry +1zostanie zapisane jako +, -1zostanie zapisane jako -i 0jest słuszne 0. Zrównoważone trójskładnikowe nie używa -symbolu przed liczbami, aby je zanegować, podobnie jak inne systemy liczbowe - patrz przykłady.

Twoim zadaniem jest napisanie kompletnego programu, który pobiera 32-bitową liczbę całkowitą ze znakiem dziesiętnym jako dane wejściowe i konwertuje ją na zbalansowany trójskładnikowy. Żadne wbudowane funkcje konwersji jakiegokolwiek rodzaju nie są dozwolone (Mathematica prawdopodobnie ma jedną ...). Dane wejściowe mogą być na standardowym wejściu, argumentach wiersza poleceń itp.

Zera wiodące mogą być obecne w danych wejściowych, ale nie w danych wyjściowych, chyba że dane wejściowe są 0, w takim przypadku dane wyjściowe również powinny być 0.

Przykłady

Są to konwersje ze zrównoważonych trójskładnikowych na dziesiętne; będziesz musiał przekonwertować w drugą stronę.

+0- = 1*3^2 + 0*3^1 + -1*3^0 = 9 + 0 + -1 = 8
+-0+ = 1*3^3 + -1*3^2 + 0*3^1 + 1*3^0 = 27 + -9 + 0 + 1 = 19
-+++ = -1*3^3 + 1*3^2 + 1*3^1 + 1*3^0 = -27 + 9 + 3 + 1 = -14
Społeczność
źródło
Och, czekaj, właśnie zauważyłem, że na zrównoważonym tuzinalu jest pytanie - czy to duplikat?
Jak wspomniałem w piaskownicy, myślę, że jest to bliższe wyzwaniu związanemu ze standardowymi reprezentacjami fińskimi . Ale prawdopodobnie nie jest to duplikat żadnego z nich.
Martin Ender

Odpowiedzi:

22

Python 2: 58 znaków

n=input()
s=""
while n:s="0+-"[n%3]+s;n=-~n/3
print s or 0

Generuje zrównoważone trójskładnikowe cyfra po cyfrze od końca. Ostatnią cyfrą jest przez reszty n%3istoty -1, 0lub +1. Następnie usuwamy ostatnią cyfrę i dzielimy przez 3, używając podziału podłogi w Pythonie n=(n+1)/3. Następnie postępujemy rekurencyjnie z nową ostatnią cyfrą, aż liczba wyniesie 0.

Potrzebny jest specjalny przypadek, aby dane wejściowe 0podawały 0zamiast pustego ciągu.


Specyfikacje na to nie pozwalają, ale jeśli ktoś mógłby napisać funkcję zamiast programu i wypisać pusty łańcuch dla 0, możliwe byłoby rozwiązanie 40 znaków.

g=lambda n:n and g(-~n/3)+"0+-"[n%3]or""
xnor
źródło
Lepiej używać n*"."andw przypadku funkcji tylko. print s or 0Działa również lepiej: P
Nabb
@Nabb Good call on s or 0. Próbowałem n*"."and, ale nie powiedzie się, kiedy n<0.
xnor
@ MartinBüttner Dłuższa odpowiedź Pytha wynikała właśnie z zastosowania nieoptymalnego algorytmu.
Optymalizator
@Optimizer Cóż, oczywiście, i dlatego głosowałem za odpowiedzią na Python, która najpierw uzyskała lepszy algorytm. : P
Martin Ender
6

CJam, 24 bajty

Wymyśliłem to niezależnie i myślę, że jest to prawdopodobnie jedyny sposób, aby sobie z tym poradzić.

li{_3%"0+-"=\_g+3/}h;]W%

Algorytmicznie jest podobny do odpowiedzi xnora.

Wypróbuj online tutaj

Jak to działa :

li{               }h                 "Read input, convert to integer and run the code block"
                                     "until stack has 0 on top";
   _3%                               "Copy and get modulus 3";
      "0+-"=                         "Take the correct character based on the above modulus";
            \_g+                     "Swap, copy and take signum of the number and add"
                                     "that to it, incrementing the number if positive,"
                                     "decrementing otherwise";
                3/                   "Integer divide by 3 and continue until 0";
                    ;]               "Pop the residual 0 and wrap everything in array";
                      W%             "Reverse to get in binary format (right handed)";
Optymalizator
źródło
Czy konieczny jest bit „przyrost, jeśli dodatni, zmniejszenie, jeśli ujemny”? dlaczego nie tylko zwiększyć?
isaacg
@isaacg Wypróbuj;)
Optymalizator
Czy zaokrąglenie podziału CJam jest inne?
isaacg
@isaacg - Zaokrąglanie - nie. Podział liczb całkowitych CJam nie zaokrągla. Podłogi
Optymalizator
Ale podłoga w kierunku zera lub -inf?
isaacg
6

JavaScript (E6) 68

Kompletny program, zgodnie z żądaniem, z I / O poprzez wyskakujące okienko. Rdzeniem jest funkcja R, 49 bajtów.

Chyba nie różni się tak bardzo od innych rozwiązań rekurencyjnych. Wykorzystując automatyczną konwersję między ciągiem a liczbą, aby uniknąć specjalnego przypadku dla „0”

 alert((R=(n,d=(n%3+3)%3)=>n?R((n-d)/3+(d>1))+'0+-'[d]:'')(prompt()))

Przetestuj w konsoli FireFox / FireBug, używając tylko funkcji R.

['0','8','19','-14','414'].map(x =>x +': '+R(x))

Wydajność

["0: 0", "8: +0-", "19: +-0+", "-14: -+++", "414: +--0+00"]
edc65
źródło
Czy coś brakuje? Jaki jest sens, d=(n%3+3)%3kiedy d=n%3daje taką samą wartość d?
RLH
@RLH nie dla wartości ujemnych (nie w JavaScript). -20% 3 === -2% 3 === -2. Zamiast tego -20 mod 3 powinien wynosić 1, a (-20% 3 + 3)% 3 to rzeczywiście 1
edc65
6

Pyth, 71 24 23

L?+y/hb3@"0+-"%b3bk|yQ0

Jest to rozwiązanie rekurencyjne, oparte na 40-znakowej funkcji rekurencyjnej @ xnor. ykonstruuje zbalansowaną trójkę wejścia, znajdując ostatnią cyfrę za pomocą indeksu mod 3, a następnie wykorzystuje fakt, że reszta cyfr jest równa zbalansowanej trójce dla (n + 1) / 3, przy użyciu dzielenia zmiennoprzecinkowego. Następnie wywołuje funkcję, zwracając wynik lub 0, jeśli wartością wejściową jest 0.

Wypróbuj tutaj.

isaacg
źródło
Czy istnieje internetowy tłumacz języka Pyth?
Dodam to do posta.
isaacg
Twój link jest uszkodzony. Polecam Wypróbuj online! , z którego będziesz korzystać odtąd, gdy będziesz potrzebować tłumacza do czegokolwiek. Nawiasem mówiąc, twój kod wydaje się nie działać, dla mnie po prostu zwraca dane wejściowe.
Pavel
@Pavel To działało dwa lata temu, kiedy to napisałem. Obecny interpreter Pyth online to pyth.herokuapp.com. Jeśli chcesz uruchomić powyższy kod, możesz sprawdzić interpreter z github.com/isaacg1/pyth , który ma pełną historię wersji kontrolowaną przez wersję.
isaacg,
3

Mathematica - 157 154 146 128

Wersja golfowa:

f=(s="";n=#;i=Ceiling@Log[3,Abs@#]~Max~0;While[i>=0,s=s<>Which[n>=(1+3^i)/2,n-=3^i;"+",n>-(1+3^i)/2,"0",1>0,n+=3^i;"-"];i--];s)&

I z naciskiem na czytelność:

f = (s = ""; n = #; i = Ceiling@Log[3, Abs@#]~Max~0;
 While[i >= 0, 
  s = s<>Which[
   n >= (1 + 3^i)/2, n -= 3^i; "+",
   n > -(1 + 3^i)/2, "0", 
   1 > 0, n += 3^i; "-"
  ];
 i--];
s)&

Stosowanie:

f[414]

Wydajność:

+--0+00

Ogromne podziękowania dla Martina Büttnera za zmniejszenie liczby znaków.

Zestawienie
źródło
3

Mathematica, 54 znaki

Podobne do rekurencji xnora

Symbole unikodowych używane do zastąpienia Floor, Part,!=

If[(t=⌊(#+1)/3⌋)≠0,#0@t,""]<>{"0","+","-"}〚#~Mod~3+1〛&

Wydajność

Przechowywany jak fdla zwięzłości i napisany bez użycia kodu Unicode, którego nie można wyświetlić

f=If[(t=Floor[(#+1)/3])!=0,#0@t,""]<>{"0","+","-"}[[#~Mod~3+1]]&
f /@ {8, 19, -14, 414} // Column

+0-
+-0+
-+++
+--0+00
mile
źródło
3

GNU sed, 236 bajtów

/^0/bV
:
s/\b9/;8/
s/\b8/;7/
s/\b7/;6/
s/\b6/;5/
s/\b5/;4/
s/\b4/;3/
s/\b3/;2/
s/\b2/;1/
s/\b1/;0/
s/\b0//
/[^;-]/s/;/&&&&&&&&&&/g
t
y/;/1/
:V
s/111/3/g
s/3\b/3:/
s/311/33!/
s/31/3+/
y/3/1/
tV
s/1/+/
y/1:/!0/
/-/{s/-//
y/+!/!+/
}
y/!/-/

Wypróbuj online!

Wyjaśnienie

Pierwsza połowa kodu (pomniejszona o pierwszą linię) tłumaczy dziesiętnie na unarny i pochodzi prosto z „ Porad dla golfa w sed” ”. Następnie tłumaczy jednoargumentowany na zrównoważony trójgłowy jeden tryt na raz, co zademonstruję, wykonując przykład ręcznie.

Przed ostatecznym wyjściem, trójoperandowy cyfr -, 0i +są reprezentowane przez !, :i +, odpowiednio.

Aby uzyskać interesujący wynik, zaczynamy od -48, który został przekonwertowany na unarny (z -nienaruszonym). Aby obliczyć pierwszy (najbardziej na prawo) trit, musimy obliczyć resztę 48 ÷ 3. Możemy to zrobić, zastępując 111s 3s:

-111111111111111111111111111111111111111111111111 │ s/111/3/g
# => -3333333333333333

48 ÷ 3 nie ma reszty, więc nie ma 1s, i wiemy, że nasz pierwszy trit to :(dla 0), więc zastępujemy go:

-3333333333333333 │ s/3\b/3:/
# => -3333333333333333:

Teraz mamy nasze „jedno miejsce”, więc wiemy, że pozostałe 3s reprezentują trzy miejsca. Aby matematyka działała, musimy podzielić je przez 3, tj. Zastąpić je 1s:

-3333333333333333: │ y/3/1/
# => -1111111111111111:

Sprawdźmy 1111111111111111dokładnie naszą matematykę: mamy 16 (unarskich ) w trójce i zero ( :) w jednym. To 3✕16 + 1✕0 = 48. Jak dotąd tak dobrze.

Teraz zaczynamy od nowa. Zamień 111s na 3s:

-1111111111111111: │ s/111/3/g
# => -333331:

Tym razem mamy resztę 1, więc umieszczamy +trójkę i zastępujemy pozostałe 3s 1s:

-333331: │ s/31/3+/; y/3/1/
# => -11111+:

Czas kontroli poczytalności: Mamy 5 (unary 11111) w dziewiątym miejscu, 1 ( +) w trójce, a 0 ( :) w jednym miejscu: 9✕5 + 3✕1 + 1✕0 = 48. Świetnie! Ponownie zamieniamy 111s na 3s:

-11111+: │ s/111/3/g
# => -311+:

Tym razem nasza reszta to 2 ( 11). To zajmuje dwie trits ( +!), co oznacza, że ​​mamy carry. Podobnie jak w przypadku arytmetyki dziesiętnej, oznacza to, że bierzemy cyfrę najbardziej na prawo i dodajemy resztę do kolumny po lewej stronie. W naszym systemie oznacza to, że umieszczamy !w dziewiątce miejsce i dodajemy kolejne trzy po jego lewej stronie, a następnie zastępujemy wszystkie 3s 1s, aby reprezentować miejsce 27.:

-311+: │ s/311/33!/; y/3/1/
# => -11!+:

Teraz nie pozostały nam żadne 3s, więc możemy zastąpić dowolne pozostałe jednoznaczne cyfry odpowiadającymi im tritami. Two ( 11) to +!:

-11!+: │ s/11/+!/
# => -+!!+:

W rzeczywistym kodzie odbywa się to w dwóch krokach s/1/+/i y/1:/!0/, aby zapisać bajty. Drugi krok zastępuje również :s 0s, więc faktycznie robi to:

-11!+: │ s/1/+/; y/1:/+0/
# => -+!!+0

Teraz sprawdzamy, czy mamy liczbę ujemną. Robimy, więc musimy pozbyć się znaku, a następnie odwrócić każdą trit:

-+!!+0 │ /-/ { s/-//; y/+!/!+/; }
# => !++!0

Na koniec zamieniamy !s na -s:

!++!0 │ y/!/-/
# => -++-0

To jest to!

Jordania
źródło
2

Stax , 17 bajtów

ë1·âΓM¿├>Ö≥Er☺à┤3

Uruchom i debuguj

Najkrótsza jak dotąd odpowiedź, ale niektóre języki gry powinny ją łatwo pokonać. Algorytm jest taki sam jak odpowiedź Python @ xnor.

Odpowiednik ASCII:

z{"0+-";3%@+,^3/~;wr
Weijun Zhou
źródło
1

JavaScript 108 102 (ES6, bez wywołania rekurencyjne)

t=a=>{v="";if(0==a)v="0";else for(a=(N=0>a)?-a:a;a;)v="0+-"[r=(a%3+3)%3]+v,2==r&&++a,a=a/3|0;return v}

Oryginalny wpis na 108

t=a=>{v="";if(0==a)v="0";else for(a=(N=0>a)?-a:a;a;)v=(N?"0-+":"0+-")[r=a%3]+v,2==r&&++a,a/=3,a|=0;return v}

Nie tak wyszukana, jak odpowiedź @ edc65 ... Byłbym wdzięczny za wszelką pomoc w zmniejszeniu tego ...

WallyWest
źródło
1

Clojure, 242 bajty

#(clojure.string/join""(map{1"+"0"0"-1"-"}(loop[x(vec(map read-string(clojure.string/split(Integer/toString % 3)#"")))](let[y(.indexOf x 2)](if(< y 0)x(let[z(assoc x y -1)](recur(if(= y 0)(vec(cons 1 z))(assoc z(dec y)(inc(x(dec y))))))))))))

Czy to jak dotąd najdłuższa odpowiedź Clojure?

Niegolfowany (z komentarzami):

(use '[clojure.string :only (split join)]);' (Stupid highlighter)
; Import functions

(defn ternary [n]
  (join ""
  ; Joins it all together
    (map {1 "+" 0 "0" -1 "-"}
    ; Converts 1 to +, 0 to 0, -1 to -
      (loop [x (vec (map read-string (split (Integer/toString n 3) #"")))]
      ; The above line converts a base 10 number into base 3,
      ; and splits the digits into a list (8 -> [2 2])
        (let [y (.indexOf x 2)]
        ; The first occurrence of 2 in the list, if there is no 2,
        ; the .indexOf function returns -1
          (if (< y 0) x
          ; Is there a 2? If not, then output the list to
          ; the map and join functions above.
            (let [z (assoc x y -1)]
            ; Converts where the 2 is to a -1 ([2 2] -> [-1 2])
              (recur
                (if (= y 0) (vec (cons 1 z))
                  ; If 2 is at the 0th place (e.g. [2 2]),
                  ; prepend a 1 (e.g. [-1 2] -> [1 -1 2])
                  (assoc z (dec y) (inc (x (dec y)))))))))))))
                  ; Else increment the previous index
                  ; (e.g. [1 -1 2] -> [1 0 -1])
clismique
źródło
1

8 , 179 171 167 znaków

Oto kompletny program na ósmym, który pobiera na wejściu liczbę całkowitą ze znakiem dziesiętnym i konwertuje ją na zbalansowany trójskładnikowy

 
: f "" swap repeat dup 3 n:mod ["0","+","-"] swap caseof rot swap s:+ swap dup n:sgn n:+ 3 n:/mod nip while drop s:rev ;
"? " con:print 16 null con:accept >n
f cr . cr
 

Test

 
? 414
+--0+00
 

Za pierwszym razem program prosi o liczbę do konwersji (w razie potrzeby). Następnie można wywołać słowo, faby przekonwertować więcej liczb, jak w następującym wierszu:

 
[ 8 , 19 , -14 , ] ( nip dup . space f . cr ) a:each drop 
 

Wydajność

 
8 +0-
19 +-0+
-14 -+++
 

Wyjaśnienie kodu

 
"? " con:print 16 null con:accept >n
 

To jest kod do obsługi danych wejściowych. Rdzeń kodu znajduje się w środku słowa f. Z dala od pola golfowego użyłbym tego słowa >btzamiast f. Oto wersja bez golfa f(z komentarzami):

 
: f \ n -- s
    ""   \ used for the first symbol concatenation
    swap \ put number on TOS to be used by loop
    repeat
        dup 
        3 n:mod      \ return the remainder of the division by 3
        [ "0" , "+" , "-" , ] 
        swap caseof  \ use remainder to take proper symbol
        rot          \ put previous symbol on TOS 
        swap         \ this is required because "" should come first
        s:+          \ concatenate symbols
        swap         \ put number on TOS 
        dup
        n:sgn n:+    \ add 1 if positive or add -1 if negative
        3 n:/mod nip \ put quotient only on TOS
    while drop
    s:rev            \ reverse the result on TOS
;
 
Dwór Chaosu
źródło
0

Java, 327 269 ​​znaków

Moja pierwsza próba gry w golfa kodu. Nie znam żadnego z tych naprawdę krótkich języków, więc oto rozwiązanie w Javie. Byłbym wdzięczny za radę, aby go jeszcze skrócić.

import java.util.*;class a{public static void main(String[] h){int i=Integer.parseInt(new Scanner(System.in).nextLine());String r="";while(i!=0){if(i%3==2||i%3==-1){r="-"+r;}else if(i%3==1||i%3==-2){r="+"+r;}else{r="0"+r;}i=i<0?(i-1)/3:(i+1)/3;}System.out.println(r);}}

Wypróbuj tutaj: http://ideone.com/fxlBBb

EDYTOWAĆ

Zastąpiony BufferedReaderprzez Scanner, pozwalając mi usunąć throwsklauzulę, ale musiałem zmienić import (+2 znaki). Zastąpiony Integerprzez int. Niestety, program nie będzie kompilować, jeśli nie ma String[] hw main.

Thrax
źródło
1
Możesz być w stanie zaoszczędzić kilka bajtów, używając Scannerzamiast swojegoBufferedReader . Również,String[] h i throws java.lang.Exceptionprawdopodobnie nie są konieczne, a możesz zaoszczędzić kilka bajtów, używając intzamiast Integer.
0

JavaScript (ES6), 51 bajtów

v=>[...v].map(x=>t=3*t+((+x!=+x)?(x+1)-0:0),t=0)&&t

Pętla przez postacie. Najpierw pomnóż poprzednie całkowite czasy 3, a następnie, jeśli isNaN (znak) jest prawdziwy, przekonwertuj ciąg (znak + „1”) na liczbę i dodaj go, w przeciwnym razie zero.

Grax32
źródło
0

APL (NARS), 26 znaków, 52 bajty

{+/(3*¯1+⍳≢⍵)ׯ2+⌽'-0+'⍳⍵}

test:

  h←{+/(3*¯1+⍳≢⍵)ׯ2+⌽'-0+'⍳⍵}
  h '+0-'
8
  h '+-0+'
19
  h '-+++'
¯14
  h ,'-'
¯1
  h ,'0'
0
  h ,'+'
1

możliwe, może być mniej, jeśli ⊥ jest używane, ale jest zabronione ...

RosLuP
źródło