Najbardziej wydajny cubizer

19

Cubically jest zbyt żmudny, aby ręcznie pisać dowolny kod. Twoim zadaniem jest przetłumaczenie tekstu ASCII na Cubicowy kod źródłowy.

Cubical

To tylko szybki przegląd Cubically; repozytorium ma bardziej kompletny podręcznik oraz szczegóły.

Cubically to esolang, który napisałem jakiś czas temu, zaprojektowany z myślą o bolesnym użyciu. Zawiera dwa fragmenty pamięci, Kostkę Rubika 3x3x3 i rejestr zwany „notatnikiem”.

Pamięć

Wewnętrzna kostka Rubika jest inicjowana w następujący sposób:

   000
   000          top face
   000
111222333444    left, front, right, and back faces, respectively
111222333444
111222333444
   555
   555          down face
   555

Po wykonaniu obrotu o 90 ° w kierunku zgodnym z ruchem wskazówek zegara na prawej powierzchni kostka pamięci wygląda następująco:

   002
   002
   002
111225333044
111225333044
111225333044
   554
   554
   554

Polecenia

Znak nie będący liczbą całkowitą ustawia polecenie domyślne. Dla każdej liczby całkowitej przed ponownym ustawieniem domyślnego polecenia polecenie jest wykonywane z tą liczbą całkowitą. Na przykład x524y312wykonałby polecenie za xpomocą 5, następnie za pomocą 2, następnie za pomocą 4, następnie wykonał polecenie za ypomocą 3, następnie za pomocą 1, a następnie za pomocą 2.

Liczby całkowite używane przez polecenia reprezentują indeksy twarzy. Tak x0by działał xna powierzchni UP (indeksowanej 0). x1działałby xna LEWEJ (1 indeksowanej) powierzchni i tak dalej.

Wykonanie dowolnego polecenia 6spowoduje wykonanie tego polecenia na wartości notatnika. Wykonanie dowolnego polecenia z dowolną liczbą całkowitą większą niż 6 spowoduje błąd.

Oto kilka przykładowych poleceń:

  • R1 - obróć PRAWĄ ściankę o 90 ° zgodnie z ruchem wskazówek zegara, aby wewnętrzny sześcian wyglądał jak drugi przykład powyżej
  • R11 - obrócić prawą twarz dwa razy w prawo o 90 °, identycznie jak R2
  • +0 - dodaj wszystkie wartości powierzchni UP do notatnika
  • +000 - trzykrotnie dodaj wszystkie wartości powierzchni UP do notatnika
  • @6 - wydrukuj nieistniejącą twarz (pamięć) z 6. indeksem jako znak
  • %4 - wydrukuj sumę wszystkich wartości na BACK face jako liczbę całkowitą

Pełna lista poleceń i składni jest dostępna w repozytorium .

Wyzwanie

Weźmiesz tekst ASCII jako dane wejściowe i wydrukujesz program Cubic jako dane wyjściowe.

Przykłady (skradzione tutaj i tutaj ):

Input -> Output
Hello, World! -> +53@6+1F2L2+0@6L2F2U3R3F1L1+2@66L3F3R1U1B3+0@6:4U1R1+00@6-000@6*0-4+000@6-00@6+2-000000@6-5+4000@6-00@6/0+00@6:0+0/0+00@6
1$2$3$4$5$6$7$8$9$10$ -> B1+2/2%6@4+00/0%6@4+00/1%6@4+21/1%6@4+30/0%6@4+22/1%6@4+22/1%6@4+40/1%6@4+52/1%6@4+42/1%6@4

Zasady

  • Twój program nie może zawierać słownika zawierającego tłumaczenia dla 100 przypadków testowych.
  • Twój program musi zakończyć się w mniej niż 180 sekund (bez programów typu brute force, które zajmują tygodnie).
  • Twój program musi wypisać prawidłowy kod sześcienny, który kończy się w mniej niż 180 sekund.
  • Twój program pobierze dane przez standardowe wejście, chyba że chcesz zadziałać ze sterownikiem testowym.
  • Twój program musi wypisywać kod sześcienny, który nie generuje nic oprócz danych wejściowych programu po uruchomieniu. ಠ_ಠ

Punktacja

Testujesz swój program za pomocą 100 pseudolosowych ciągów o długości pseudolosowej. (Dostępny jest skrypt bash, który zrobi to za Ciebie.) Oto, jak zdobędziesz punkty:

  • Niech długość programu wyjściowego być o .
  • Niech długość ciągu wejściowego będzie równa l .
  • Niech zmienna r będzie wynikiem o / l .
  • Znajdź średnią wszystkich r : (r 1 + r 2 + r ... + r 100 ) / 100 .

Przetestuj za pomocą tego skryptu. Będziesz musiał go zmodyfikować zgodnie z instrukcją. Zauważ, że program nie sprawdza, czy dane wyjściowe są poprawne Kod sześcienny. Jeśli nie możesz uruchomić skryptu, mogę pomóc. Zawołaj mnie do pokoju czatu Cubically .

MD XF
źródło
Czy ” @6- wydrukowałby sumę nieistniejącej twarzy z 6 indeksami (notatnika) jako znak„ byłby dokładniejszy? Czy to %4także suma? Czy +polecenia sumują twarz, a następnie dodają to do wszystkich wartości czy ...?
Jonathan Allan
@JonathanAllan @6/ %6po prostu drukuje wartość notatnika jako znak / liczbę całkowitą. @x/ %x(gdzie x to dowolna istniejąca twarz) dodaje wszystkie wartości do xtwarzy -indexed i drukuje sumę jako znak / liczbę całkowitą. +dodaje do rejestru wszystkie wartości z określonej twarzy.
MD XF,
Ach, z jakiegoś powodu myślałem, że notatnik ma też 9 wartości.
Jonathan Allan

Odpowiedzi:

4

C ++ 11, wynik : 6,37

#include <iostream>
#include <vector>
#include <array>
#include <limits>
#include <algorithm>

const int inf = std::numeric_limits<int>::max(),
maxDiff = 128, nFace = 6;
std::array<int, maxDiff+1> plusvalue, totalvalue, plustrace, totaltrace;
std::array<int, nFace> input;

void prtrace(int value) {
    while (value) {
        std::cout << plustrace[value];
        value -= input[plustrace[value]];
    }
}

void prexpr(int i) {
    char xorwt = 0;
    if (i < 0) {
        xorwt = '+' ^ '-';
        i = -i;
    }
    if (totalvalue[i] != 0 && totalvalue[i] != inf) {
        std::cout << (char)('+' xor xorwt);
        prtrace(totaltrace[i]);
        if (totaltrace[i] != i) {
            std::cout << (char)('-' xor xorwt);
            prtrace(totaltrace[i] - i);
        }
    }
}

int main() {
    std::cout << "RU";
    input = {6, 15, 27, 26, 19, 42};
    std::cin >> std::noskipws;

    std::fill(plusvalue.begin(), plusvalue.end(), inf);
    plusvalue[0] = 1; // '+'
    for (int i = 0; i < nFace; ++i) { // knapsack, each value repeated inf times
        int val = input[i];
        if (val == 0) continue;
        for (int p = 0; p <= maxDiff - val; ++p) {
            if (plusvalue[p] != inf && plusvalue[p + val] > plusvalue[p] + 1) {
                plusvalue[p + val] = plusvalue[p] + 1;
                plustrace[p + val] = i;
            }
        }
    }
    for (int p = 0; p <= maxDiff; ++p) totalvalue[p] = plusvalue[p], totaltrace[p] = p;
    totalvalue[0] = 0;
    for (int sub = 1; sub <= maxDiff; ++sub) {
        if (plusvalue[sub] == inf) continue;
        for (int p = 0; p <= maxDiff - sub; ++p) {
            if (plusvalue[p+sub] != inf && totalvalue[p] > plusvalue[p+sub] + plusvalue[sub]) { // include '+'s
                totalvalue[p] = plusvalue[p+sub] + plusvalue[sub];
                totaltrace[p] = p+sub;
            }
        }
    }

//    // Note: plustrace[x] = i<=nFace : plustrace[x-input[i]] + 1 = plustrace[x]
//    long long sum = 0;
//    for (int i = 0; i <= maxDiff; ++i) {
//        sum += totalvalue[i];
//        std::cout << i << '\t' << totalvalue[i] << '\t';
//        prexpr(i);
//        std::cout << '\n';
//    }
//
//    std::cout << "_______________________________\n\nAverage = " << sum / (maxDiff + 1.) << '\n';

// RU 3.98131

    char ch;
    int cur = 0;
    while (std::cin >> ch) {
        int diff = ch - cur;
        prexpr(diff);
        std::cout << "@6";
        cur += diff; // cur = ch
    }
}

/*
RU 3.98131
*/

Wypróbuj online! (generuj kod Cubical z ASCII) i (uruchom kod Cubically)

Wyjaśnienie:

  • Najpierw program wypisuje „RU”, które obliczają sumę twarzy od {0,9,18,27,36,45}do {6, 15, 27, 26, 19, 42}. Tym, co sprawia, że ​​zestaw sum twarzy jest użyteczny, jest to, że gcd ma wartość 1, więc dzięki tożsamości Bézout istnieje sposób na skonstruowanie dowolnej liczby dz sumy (lub różnicy) tych liczb.
  • Dlatego jeśli następny znak jest, cha bieżąca wartość notatnika n, to pozwólmy d = ch - n, możemy wykonać polecenia Cubically w formie +{digits from 0 to 5}-{digits from 0 to 5}, w której staje się wartość notatnika ch. Następnie wystarczy wykonać, %6aby wydrukować wartość notatnika.
  • Aby znaleźć najbardziej efektywny sposób wyrażania djako suma / różnica liczb w zestawie sumy twarzy, używam algorytmu Knapsack dla wszystkich liczb od 0 do 128. Np. d=1Program pobiera 27 - 26 = 1, więc drukuje +2-3, czyli jest 27 - 26 = 1. Co można zobaczyć po uruchomieniu programu z wejściem abc, wyjściem programu

    RU + 4333 @ 6 + 2-3 @ 6 + 2-3 @ 6

użytkownik202729
źródło
Woah, świetna robota! Algorytm Knapsack jest chyba tym, czego szukaliśmy.
TehPers
Pamiętaj, że z powodu aktualizacji języka możesz uzyskać lepszy wynik, dzwoniąc @niejawnie - w każdym przypadku @6można go skrócić @.
MD XF,
17

Lua, wynik : 85,91 13,50 13,20 12,70 9,41 9,32 9,83 9,66 9,12 9,06 8,03 (średnio)

-- Get input
local inp = io.read("*a")

local out = ""
local faces = { [5] = 45, [4] = 36, [3] = 27, [2] = 18, [1] = 9 }
local lastChar = nil

-- Mode the program is in
-- 2 = not set (needs :), 1 = just set (needs +), 0 = normal
local mode = 1;
for i = 1, inp:len() do
  -- Character value at current position
  local c = string.byte(inp, i)

  if c == lastChar then
    -- Repeat character
    out = out .. "6"
  elseif c % 9 == 0 and c <= 45 then
    if #out == 0 then
      out = out .. "@"
    end
    out = out .. (c / 9)
  else
    local c2 = c

    -- Handle if difference from lastChar is divisible by 9
    if lastChar and (c - lastChar) % 9 == 0 then
      local difference = c - lastChar
      if difference > 0 then
        out = out .. "+"
      else
        out = out .. "-"
        difference = difference * -1
      end

      for index = 5, 1, -1 do
        local face = faces[index]
        while difference >= face do
          difference = difference - face
          out = out .. index
        end
      end
      c = 0
    end

    -- Handle anything not divisible by 9
    local extra = c % 9
    if extra > 0 then
      -- Try to optimize by dividing by 9, if possible
      if lastChar and math.floor(lastChar / 9) == extra then
        out = out .. "/1"
        mode = 1
        extra = 0
      else
        while extra > 0 do
          local n = extra > 5 and 5 or extra

          if mode == 2 then
            out = out .. ":"
            mode = 1
          elseif mode == 1 then
            out = out .. "+"
            mode = 0
          end
          out = out .. n
          extra = extra - n
        end
        out = out .. "/1"
        mode = 1
      end

      c = c - (c % 9)
    end

    -- Handle anything divisible by 9
    for index = 5, 1, -1 do
      local face = faces[index]
      while c >= face do
        if mode == 2 then
          out = out .. ":"
          mode = 1
        elseif mode == 1 then
          out = out .. "+"
          mode = 0
        end
        c = c - face
        out = out .. index
      end
    end

    out = out .. "@6"
    lastChar = c2
  end

  mode = 2
end
print(out)

Wypróbuj online!

Ok, nie sądzę, żebym mógł to zoptymalizować.

Ta wersja wykonuje iterację po każdym znaku, dodając c% 9 (gdzie c jest wartością dziesiętną znaku) :5+2/1, a następnie dodaje części podzielne przez 9, dodając wartość tej twarzy. Na przykład: :2/1+551@aby wydrukować „e”, gdzie :2/1dodaje 2, +551dodaje 99 (9 * (5 + 5 + 1) lub 9 * 11) i @drukuje wydruk. Wejście jest czytane za pomocą io.read().

Optymalizacje obejmują bezpośrednie dodawanie / odejmowanie po wydrukowaniu, jeśli różnica między znakami jest wielokrotnością 9, dzielenie bieżącej wartości, jeśli to możliwe, zamiast ustawiania c% 9 od zera, i powtarzanie znaków poprzez ponowne wydrukowanie bieżącej wartości zamiast jej ponownego obliczenia. Dodatkowo wdrożyłem metodę Kamila do natychmiastowego drukowania dowolnej twarzy, która już zawiera wartość docelową, oraz sugestię MD XF, aby nie używać :na początku, ale zamiast tego po prostu zacznij od +.

TehPers
źródło
1
Zawsze możesz komentować własne pytania i odpowiedzi, ale nie masz jeszcze ogólnych uprawnień do komentowania. Nie powinno być długo z takimi odpowiedziami (lub, co bardziej prawdopodobne, tylko ta odpowiedź, gdy zobaczy ją jeszcze kilka osób)
Kamil Drakari,
2
@MDXF Nie jestem aż tak miły: P
Kamil Drakari
1
Możesz zmienić local inp = io.read()na local inp = io.read("*all"). To rozwiązuje problem.
MD XF,
1
Kolejna możliwa optymalizacja - ponieważ notatnik zaczyna się od 0, tak naprawdę nie musisz generować danych wyjściowych, np. :5+124Możesz po prostu pisać +5124, co prawdopodobnie nieco obniży wynik, jeśli poprawisz go poprawnie.
MD XF,
1
Prawdopodobnie uzyskasz lepszy wynik, jeśli zmienisz odpowiedź, aby obsługiwać niektóre najnowsze aktualizacje Cubically, takie jak niejawne skręty twarzy.
MD XF
16

Sześciennie , wynik : 86,98

U3D1R3L1F3B1U1D3~:7+1(-1@3(-1%1)6:1+3111@6%1-31111+004@6:1+11111%6:1+45@6:1-1%6~:7+1)6 

Wypróbuj online!

Okazuje się, że wszystko, czego potrzebujesz, to pętle warunkowe, twarz równa 1 i spójne zachowanie na końcu wejścia.

U3D1R3L1F3B1U1D3     set LEFT face sum to 1
~:7                  read input, set notepad to input
+1                   add 1 to notepad

(                    open loop that can always be jumped to
 -1                   subtract 1 from notepad
 @3                   print RIGHT face (ASCII 43 '+')

            ##   the following mechanism gets the output program's   ##
            ##   notepad to the current inputted ASCII value:        ##

 (                    open loop that can always be jumped to
  -1                   subtract 1 from notepad
  %1                   print '1'
 )6                   jump back to loop while notepad is nonzero

            ##   the following mechanism prints "/1@6:1"             ##

 :1+3111@6            set notepad to 47,    print (ASCII 47 '/')
 %1                   print LEFT face       print (integer '1')
 -31111+004@6         set notepad to 64,    print (ASCII 64 '@')
 :1+11111%6           set notepad to 6,     print (integer 6)
 :1+45@6              set notepad to 58,    print (ASCII 58 ':')
 :1-1%6               set notepad to 0,     print (integer 1)

 ~                    read input
 :7+1                 set notepad to input plus 1, so EOF changes to zero
)6                    loop if notepad is truthy

Dodawanie / odejmowanie LEWEJ powierzchni powoduje, że pętla kończy się po odczytaniu EOF.

Kamil Drakari
źródło
2
You have got być żart. To jest niesamowite.
MD XF,
Och, hej, ma nawet lepszy wynik niż moja pierwotna odpowiedź w języku C #!
Kamil Drakari,
Pamiętaj, że z powodu aktualizacji języka możesz uzyskać lepszy wynik, dzwoniąc @niejawnie - w każdym przypadku @6można go skrócić @.
MD XF,
9

C # (.NET Core) , wynik: 129,98 11,73 10,82 9,62 10,33 10,32 10,20

-1.2 punktu od sugestii MD XF, aby użyć @6666...zamiast @6@6@6@6...do powtarzania znaków i lepszej sekwencji inicjalizacji

static void Main()
{
	List<byte> input = new List<byte>();
            int inChar = Console.Read();
            while (inChar != -1)
            {
                input.Add((byte)inChar);
                inChar = Console.Read();
            }


            Console.Write("U3D1R3L1F3B1U1D3");
            byte[] sides = new byte[] { 20, 1, 14, 43, 24, 33 };

            byte currentChar = 0;

   	    if(currentChar == input[0] || sides.Contains(input[0])) Console.Write("@");

            foreach (byte character in input)
            {
		if (currentChar == character)
		{
			Console.Write("6");
			continue;
		}
		
		if (sides.Contains(character))
		{
			Console.Write(Array.IndexOf(sides, character));
			continue;
		}
                if (currentChar < character)
                {
                    Console.Write("+");
                    while (currentChar < character)
                    {
                        byte nextAdd = sides.Where(s => s + currentChar <= character).Max();
                        currentChar = (byte)(currentChar + nextAdd);
                        Console.Write(Array.IndexOf(sides, nextAdd));
                    }
                }

                if (currentChar > character)
                {
                    Console.Write("-");
                    while (currentChar > character)
                    {
                        byte nextSub = sides.Where(v => currentChar - v >= character).Max();
                        currentChar = (byte)(currentChar - nextSub);
                        Console.Write(Array.IndexOf(sides, nextSub));
                    }
                }

                Console.Write("@6");
            }
}

Wypróbuj online!

Moja najnowsza wersja faktycznie manipuluje kostką! Tak!

Po pierwsze, Console.Writeopracowano naprawioną manipulację MD XF, która tworzy tę kostkę:

   242
   202
   242
000131555313
010121535343
000131555313
   424
   454
   424

Znaczenie tego sześcianu polega na tym, że jedna z jego boków ma sumę 1, co pozwala na manipulowanie Notatnikiem na mniejszą skalę niż wielokrotność dziewięciu, a w szczególności upraszcza względny ruch, zamiast konieczności rozpoczynania od zera każdego znaku; w tym algorytmie zarówno dodawanie, jak i odejmowanie są używane w celu uzyskania najkrótszej ścieżki między znakami.

Wersja inicjalizacji MD XF powoduje, że strona 2 ma sumę 14, co oszczędza wiele bajtów danych wyjściowych dla odległości ASCII między 14 a 20.

Teraz Console.Read () obsługuje teraz dane wejściowe z wewnętrznymi znakami nowej linii, aż do końca pliku; zobacz link TIO, który powinien mieć wejście

Hello, 
World!

Ogoliłem kilka ułamków punktu, natychmiast wypisując znak, jeśli jego wartość ASCII akurat istnieje po stronie.

Skrypt testowy dzięki uprzejmości MDXF


Poprzednie zgłoszenie tutaj i wyjaśnienie:

To trochę nudne, ale o ile mogę powiedzieć, że działa. Wprawdzie tylko próbowałem, Hello, World!ale uruchomiłem dane wyjściowe w interpreterie sześciennym TIO i wyszło „Witaj, świecie!” więc założyłem, że to działa.

Zamiast faktycznie manipulować kostką, notatnik jest po prostu wielokrotnie zwiększany o sumę 1 powierzchni (9), aż będzie miała odpowiednią wartość dla każdego znaku, a następnie drukuje go.

Kamil Drakari
źródło
Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
Martin Ender
@MartinEnder Czy możesz zamiast tego przenieść je do istniejącego pokoju rozmów ?
MD XF,
@MDXF Mógłbym, ale nie mogę powiedzieć, czy skończyłyby na tym, że zupełnie nie pasują do tego miejsca i kontekstu w tym czacie.
Martin Ender
@MartinEnder Komentarze są starsze niż pokój czatu, więc pojawiały się w zapisie, prawda?
MD XF
Oni by. Dzięki, przeniosę wiadomości.
Martin Ender