21
\$\begingroup\$

Background

The move-to-front transform (MTF) is a data encoding algorithm designed to improve the performance of entropy encoding techniques.

In the bzip2 compression algorithm, it is applied after the Burrows–Wheeler transform (as seen in Burrows, Wheeler and Back), with the objective of turning groups of repeated characters into small, easily compressible non-negative integers.

Definition

For the purpose of this challenge, we'll define the printable ASCII version of the MTF as follows:

Given an input string s, take an empty array r, the string d of all printable ASCII characters (0x20 to 0x7E) and repeat the following for each character c of s:

  1. Append the index of c in d to r.

  2. Move c to the front of d, i.e., remove c from d and prepend it to the remainder.

Finally, we take the elements of r as indexes in the original d and fetch the corresponding characters.

Step-by-step example

INPUT: "CODEGOLF"

0. s = "CODEGOLF"
   d = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
   r = []
1. s = "ODEGOLF"
   d = "C !\"#$%&'()*+,-./0123456789:;<=>?@ABDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
   r = [35]
2. s = "DEGOLF"
   d = "OC !\"#$%&'()*+,-./0123456789:;<=>?@ABDEFGHIJKLMNPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
   r = [35 47]
3. s = "EGOLF"
   d = "DOC !\"#$%&'()*+,-./0123456789:;<=>?@ABEFGHIJKLMNPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
   r = [35 47 37]
4. s = "GOLF"
   d = "EDOC !\"#$%&'()*+,-./0123456789:;<=>?@ABFGHIJKLMNPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
   r = [35 47 37 38]
5. s = "OLF"
   d = "GEDOC !\"#$%&'()*+,-./0123456789:;<=>?@ABFHIJKLMNPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
   r = [35 47 37 38 40]
6. s = "LF"
   d = "OGEDC !\"#$%&'()*+,-./0123456789:;<=>?@ABFHIJKLMNPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
   r = [35 47 37 38 40 3]
7. s = "F"
   d = "LOGEDC !\"#$%&'()*+,-./0123456789:;<=>?@ABFHIJKMNPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
   r = [35 47 37 38 40 3 45]
8. s = ""
   d = "FLOGEDC !\"#$%&'()*+,-./0123456789:;<=>?@ABHIJKMNPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
   r = [35 47 37 38 40 3 45 41]

OUTPUT: "COEFH#MI"

Task

Write a program or function that implements the printable ASCII MTF (as defined above).

Test cases

Input:  Programming Puzzles & Code Golf
Output: Prpi"do lp%((uz rnu&3!P/o&$U$(p

Input:  NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN BATMAN!
Output: Na! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !!"DDUP"%'

Input:  Two more questions and I have bzip2 in less than 100 bytes!
Output: Twp#o"si$sv#uvq(u$(l#o#W!r%w+$pz,xF%#,"x(. #0--'$GG ".z(**:

Additional rules

  • You cannot use any built-in operator that computes the MTF of a string.

  • Your code may print a trailing newline if you choose STDOUT for output.

  • Your code has to work for any input of 1000 or less printable ASCII characters (0x20 to 0x7E).

  • Standard code golf rules apply. The shortest submission in bytes wins.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ "Nanananana DDUP!" just isn't as catchy as "Batman!"... \$\endgroup\$
    – Doorknob
    Jul 3 '15 at 22:12
  • 8
    \$\begingroup\$ @Doorknob: But Batman isn't easily compressible. \$\endgroup\$
    – Dennis
    Jul 3 '15 at 22:13
  • \$\begingroup\$ Can we output the result in a function return instead of printing it to STDOUT? \$\endgroup\$
    – Fatalize
    Jul 3 '15 at 22:39
  • \$\begingroup\$ @Fatalize: That's the most natural form of output for functions, so yes. By the way, we have defaults for I/O, so unless the question explicitly says otherwise, that's always allowed. \$\endgroup\$
    – Dennis
    Jul 3 '15 at 22:43

10 Answers 10

6
\$\begingroup\$

CJam, 20

'¡,q{_C#c' ,C+@|}fC;

Try it online

Explanation:

'¡,      make a string of characters with codes from 0 to 160 (a modified "d")
         could have been to 126 but stackexchange doesn't like the DEL character
q        read the input (s)
{…}fC    for each character C in s
  _      duplicate the d string
  C#     find the index of C in d
  c      convert to character (this is the result)
  ' ,    make a string of characters from 0 to 31
  C+     append C to the string
  @      bring d to the top
  |      set union, preserving order; effectively, C is moved to position 32
         this is the updated d string
;        pop the last d
\$\endgroup\$
6
\$\begingroup\$

Ostrich, 46 45 chars

Don't have a version number in the header because this is actually just the latest commit. I added the O (ascii code to string) operator after releasing the latest version (but still before this challenge was posted).

{a95,{32+O}%:d3@{:x\.3@?3@\+\x-x\+}/;{d=}%s*}

Explanation:

a             this is the "r" array (a is short for [], empty array)
95,{32+O}%:d  this is the "d" array
3@{...}/      for each character in the input (as an "argument")...
  :x            store in variable x (stack is now [r d c])
  \.3@?         find index in d     (stack is now [r d idx])
  3@\+          append index to r   (stack is now [d modified_r])
  \x-           remove char from d, and then...
  x\+           prepend char to d   (stack is now [modified_r modified_d])
;             throw away modified_d
{d=}%         map r to indices of (original) d
s*            join (s is short for ``, empty string)
\$\endgroup\$
3
  • \$\begingroup\$ I'm wondering if PPCG is turning from "code this task in the most conscise way possible in your favourite language" to "design your own programming language to solve the typical code golf task shorter than golfscript" \$\endgroup\$ Jul 5 '15 at 12:30
  • 1
    \$\begingroup\$ @AlexA. ... wait, huh, it's spelled that way? my entire life has been a lie \$\endgroup\$
    – Doorknob
    Jul 6 '15 at 0:41
  • \$\begingroup\$ @JanDvorak Ostrich is almost identical to GolfScript. Only real reason I created it is because a.) GolfScript annoyingly does not have a REPL and b.) there are a few missing operators/features (floating point, I/O, etc). And language design is fun anyway! \$\endgroup\$
    – Doorknob
    Jul 6 '15 at 0:43
3
\$\begingroup\$

SWI-Prolog, 239 197 189 bytes

a(S):-l([126],X),a(S,X,[],R),b(R,X).
a([A|T],X,S,R):-nth0(I,X,A,Z),(a(T,[A|Z],[I|S],R);R=[I|S]).
b([A|T],X):-(b(T,X);!),nth0(A,X,E),put(E).
l([B|R],Z):-A is B-1,X=[A,B|R],(A=32,Z=X;l(X,Z)).

Example: a(`Two more questions and I have bzip2 in less than 100 bytes!`). outputs:

Twp#o"si$sv#uvq(u$(l#o#W!r%w+$pz,xF%#,"x(. #0--'$GG ".z(**:

(and true . after it, obviously)

Note: your SWI-Prolog version has to be one of the newer ones in which the backquote ` represents codes strings. Code strings used to be represented with double-quotes " in older versions.

\$\endgroup\$
3
\$\begingroup\$

Python 3, 88

*d,=range(127)
for c in input():y=d.index(ord(c));d[:32]+=d.pop(y),;print(chr(y),end='')

Using some ideas from my CJam solution.
-4 bytes belong to Sp3000 :)

\$\endgroup\$
2
\$\begingroup\$

Python 2, 137 110 104

Wasn't hard to implement, but maybe still golfable?

Try it here

e=d=map(chr,range(32,127))
r=""
for c in raw_input():n=e.index(c);r+=d[n];e=[e[n]]+e[:n]+e[n+1:]
print r
\$\endgroup\$
4
  • 1
    \$\begingroup\$ I think you're better off doing a list map e=d=map(chr,range(32,127)) in Python 2, though you have to tweak the e to handle a list. \$\endgroup\$
    – xnor
    Jul 4 '15 at 9:37
  • \$\begingroup\$ @xnor Thanks. I also tried using e=[e.pop(n)]+e, but it doesn't work. Why is that? \$\endgroup\$
    – mbomb007
    Jul 6 '15 at 17:25
  • \$\begingroup\$ You've got e=d=, so when you pop from e you're also popping from d. Try d=e[:]. \$\endgroup\$
    – Sp3000
    Jul 6 '15 at 17:47
  • 1
    \$\begingroup\$ But at this point it's probably better to just do n=e.index(ord(c));r+=chr(n+32); and drop d \$\endgroup\$
    – Sp3000
    Jul 6 '15 at 17:51
2
\$\begingroup\$

Stax, 18 15 14 13 bytes

ä╦@╥◄/J[;&πi⌡

Run and debug it

-3 bytes using a map, and a simpler algorithm.

-1 byte borrowing from Kevin Cruijssen's answer.

-1 byte by simplifying a stack operation.

\$\endgroup\$
2
\$\begingroup\$

Perl 5, 96 bytes

sub{$d=$o=join'',map chr,32..126;join'',map{$d=~s/\Q$_//;$d=$_.$d;substr$o,length$`,1}pop=~/./g}

Try it online!

Ungolfed with some explanation:

sub{
  $d=$o=join'',map chr,32..126;  #init strings d and o to chars between 0x32-0x7e
  join('',                       #join array of chars to string
    map {                        #map array of input chars
      $d=~s/\Q$_//;              #pick out current input char from d, \Q to avoid
                                 #...special regexp meaning of some chars
      $d=$_.$d;                  #prepend current input char to d
      substr$o,length$`,1        #map to char in original dict o at position of
                                 #...current input char in d
    }
    pop=~/./g                    #array of input chars
  )                              #return joined chars
}

The \Q isn't needed for the given test cases since they don't contain . (period) or other chars with special meaning in regular expressions. If they did, \Q is needed.

\$\endgroup\$
1
\$\begingroup\$

Pyth, 24 bytes

JK>95CM127s@LKxL~J+d-Jdz

Demonstration. Test Harness.

The first bit. JK>95CM127 sets up the necessary list and saves it to J and K. ~J+d-Jd performs the list updating, while xL ... z maps the input characters to their positions in the list. Finally, s@LK converts those indexes to characters in the original list.

\$\endgroup\$
1
\$\begingroup\$

Haskell, 120 bytes

e#s=[b|(b,a)<-zip[0..]s,a==e]!!0
a=[' '..'~']
f=snd.foldl(\(d,r)e->(e:take(e#d)d++tail(drop(e#d)d),r++[a!!(e#d)]))(a,[])

Usage example: f "CODEGOLF" -> "COEFH#MI"

How it works: # is an index function that returns the position of e in s (can't use Haskell's native elemIndex because of an expensive import). The main function f follows a fold pattern where it updates the position string d and result string r as it walks through the input string.

\$\endgroup\$
1
\$\begingroup\$

05AB1E, 16 bytes

žQDUIεXskXyìÙU}è

Output as a list of characters.

Try it online or verify all test cases.

Explanation:

žQ           # Push the printable ASCII constant string
  DU         # Duplicate it, and pop and store this copy in variable `X`
    Iε       # Map over the characters of the input:
      Xsk    #  Get the index of the current character in `X`
      Xyì    #  Then prepend the current character to `X`
         Ù   #  Uniquify the characters in this new string
          U  #  Pop and store it as new `X`
     }è      # After the map: index the mapped integers into the printable ASCII string
             # (after which the resulting character list is output implicitly)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.