23
\$\begingroup\$

Write the shortest program possible such that when you combine the first character and every Nth character after it into a new program, the output is N. This must work for N = 1, 2, ..., 16.

Another way to say it is, if you remove all the characters from your program except for the first one and every Nth one after that, the output of the remaining code should be N.

Example

If your code was

ABCDEFGHIJKLMNOP

N = 1 results in ABCDEFGHIJKLMNOP. Running this should output 1.
N = 2 results in ACEGIKMO. Running this should output 2.
N = 3 results in ADGJMP. Running this should output 3.
N = 4 results in AEIM. Running this should output 4.
N = 5 results in AFKP. Running this should output 5.
N = 6 results in AGM. Running this should output 6.
N = 7 results in AHO. Running this should output 7.
N = 8 results in AI. Running this should output 8.
N = 9 results in AJ. Running this should output 9.
N = 10 results in AK. Running this should output 10.
N = 11 results in AL. Running this should output 11.
N = 12 results in AM. Running this should output 12.
N = 13 results in AN. Running this should output 13.
N = 14 results in AO. Running this should output 14.
N = 15 results in AP. Running this should output 15.
N = 16 results in A. Running this should output 16.

Details

  • All characters are allowed, ASCII and non-ASCII. (Newlines and unprintable ASCII are allowed as well. Note that carriage return and line feed count as distinct characters.)
  • Your score is the length in characters of your unaltered program (15 in example). The lowest score wins.
  • A score below 16 is clearly impossible because then at least two of the altered programs would be identical.
  • Output may be to a file or stdout or anything else reasonable. However, the output of the 16 different programs must all go to the same place (e.g. it's not ok if AO goes to stdout but A goes to a file). There is no input.
  • The output must be in decimal, not hex. The actual output should only contain the 1 or 2 characters that make up the number from 1 to 16, nothing else. (Things like Matlab's ans = are ok.)
  • Your program does not have to work for N = 17 or above.
\$\endgroup\$
8
  • \$\begingroup\$ Your stance on comments in the program? Yay or nay? \$\endgroup\$
    – AndoDaan
    Commented Aug 19, 2014 at 3:41
  • 1
    \$\begingroup\$ @AndoDaan Yay . \$\endgroup\$ Commented Aug 19, 2014 at 3:47
  • \$\begingroup\$ What about newlines, are those treated as characters? Also, what about outputting unprintable characters to STDOUT additionally to the numbers? \$\endgroup\$
    – overactor
    Commented Aug 19, 2014 at 4:50
  • 3
    \$\begingroup\$ It looks like when code is ABCDEFGHIJKLMNO and N = 15, result code is just A. \$\endgroup\$
    – Snack
    Commented Aug 19, 2014 at 4:54
  • \$\begingroup\$ @githubphagocyte and Snack Fixed the examples and unclear parts. Thanks! \$\endgroup\$ Commented Aug 19, 2014 at 14:24

7 Answers 7

22
\$\begingroup\$

Befunge 93 - Five million seven-hundred sixty-five thousand and seven-hundred and seventy six characters

I demand to be taken seriously...

v                                                                               &(720 720 - 80) X SPACE
""""""""""""""""                                                                &(720 720 - 80) X SPACE
1234567890123456                                                                &(720 720 - 80) X SPACE
"""""""""1111111                                                                &(720 720 - 80) X SPACE
,,,,,,,,,"""""""                                                                &(720 720 - 80) X SPACE
@@@@@@@@@,,,,,,,                                                                &(720 720 - 80) X SPACE
         ,,,,,,,                                                                &(720 720 - 80) X SPACE
         @@@@@@@                                                                &(720 720 - 80) X SPACE

3 reasons why. 1st reason: a befunge script is always 80x25, so no matter what, there had to be something that got reduced on the lines with code. 2nd reason: why that something is about 5.5 million spaces is because 720 720 is the smallest common multiple of 1 to 16... Means there will be no wrap around mess when we're skipping characters. 3rd reason: wow, this is pretty absurd.

\$\endgroup\$
22
\$\begingroup\$

Python - 1201 1137 (generator: 241 218) - Long live the hashes!

Strategy:

I tried to start every line with as many hashes as the desired output n. Then all other versions will skip this line completely.

The main difficulty, however, was to append the correct number of hashes so that the next run will hit the beginning of the next line exactly. Furthermore, interferences with other versions might occur, e.g. version 16 jumping right into the print command of line 5 and so on. So this was a lot of trial and error combined with a helper script for rapid testing.

Statistics:

  • Characters: 1201 1137
  • Hashes: 1066 1002 (88.1 %)
  • Non-hashes: 135 (11.9 %)

Code:

#
print 1#####
#p#r#i#n#t# #2######################
##p##r##i##n##t## ##3###
###p###r###i###n###t### ###4
####p####r####i####n####t#### ####5#########
#####p#####r#####i#####n#####t##### #####6##########
######p######r######i######n######t###### ######7###########
#######p#######r#######i#######n#######t####### #######8###
########p########r########i########n########t######## ########9##
#########p#########r#########i#########n#########t######### #########1#########0##
##########p##########r##########i##########n##########t########## ##########1##########1##
###########p###########r###########i###########n###########t########### ###########1###########2##
############p############r############i############n############t############ ############1############3##
#############p#############r#############i#############n#############t############# #############1#############4##
##############p##############r##############i##############n##############t############## ##############1##############5##
###############p###############r###############i###############n###############t############### ###############1###############6

Test script:

with open('printn.py', 'r') as f:
    c = f.read()

for n in range(1, 17):
    print "n =", n, "yields",
    exec c[::n]

Output:

n = 1 yields 1
n = 2 yields 2
n = 3 yields 3
n = 4 yields 4
n = 5 yields 5
n = 6 yields 6
n = 7 yields 7
n = 8 yields 8
n = 9 yields 9
n = 10 yields 10
n = 11 yields 11
n = 12 yields 12
n = 13 yields 13
n = 14 yields 14
n = 15 yields 15
n = 16 yields 16

Update: A generating script!

I thought about my solution and that there must be a pattern to generate it algorithmically. So here we go:

lines = ['#']
for i in range(1, 17):
    lines.append(('#' * (i - 1)).join('\nprint ' + `i`))
    fail = True
    while fail:
        while ''.join(lines)[::i].find('print ' + `i`) < 0:
            lines[i] = '#' + lines[i]
        fail = False
        for n in range(1, 17):
            try:
                exec ''.join(lines)[::n]
            except:
                lines[i] = '#' + lines[i]
                fail = True
                break
print ''.join(lines)

It builds the program line by line:

  1. Start with a hash.
  2. Append a new line i with the print i command and i - 1 hashes in between each two neighboring characters.
  3. While the "i-version" (every i-th character) of the current program does not contain the command print i (due to misalignment) or any n-version with n in range(1, 17) throws an exception, add another hash to the previous line.

It actually returned a shorter program than I found manually this morning. (So I updated my solution above.) Furthermore, I'm pretty sure there is no shorter implementation following this pattern. But you never know!

Golfed version - 241 218:

h='#';L=[h];I=range(1,17);J=''.join
for i in I:
 p='print '+`i`;L+=[(h*(i-1)).join('\n'+p)]
 while 1:
  while J(L)[::i].find(p)<0:L[i]=h+L[i]
  try:
   for n in I:exec J(L)[::n]
   break
  except:L[i]=h+L[i]
print J(L)

Note that there might be a shorter generator, e.g. by hard-coding the required number of succeeding hashes for each line. But this one computes them itself and could be used for any N > 16.

\$\endgroup\$
3
  • 3
    \$\begingroup\$ The character is usually called "hash" (or "octothorpe" if you feel fancy, or "number sign") \$\endgroup\$
    – FireFly
    Commented Aug 19, 2014 at 11:43
  • \$\begingroup\$ Nice work! Using Ruby you could shorten it a lot by using p 1 instead of print 1. \$\endgroup\$ Commented Aug 19, 2014 at 17:07
  • 1
    \$\begingroup\$ Yes, absolutely! In terms of code golf this might be Python's major weakness. - But thanks to AndoDaan's 5765776-character solution my code length is still far beyond average! :) \$\endgroup\$
    – Falko
    Commented Aug 19, 2014 at 17:09
16
\$\begingroup\$

209 characters (Various languages)

I just tried to keep things simple and avoid putting anything in positions with a lot of prime factors. The advantage is an ability to run in many scripting languages. It should work in any language that is not deliberately perverse and has the following features:

  • Integer literals
  • Basic arithmetic operators +, - (subtraction and negation), *, /
  • Prints the evaluation of a bare expression
  • Has a single character that begins a line comment

For example,

Python 2 command-line interpreter (though not from a file):

+                1 #            4 /   23      #    #   5            #            9   #            7   6 *         #    # -        5     2   *  -        ##  2        6   #2                     *   2       6   4

MATLAB (simply replace '#' with '%'):

                 1 %            4 /   23      %    %   5            %            9   %            7   6 *         %    % -        5     2   *  -        %%  2        6   %2                     *   2       6   4

N.B. There should be 17 spaces preceding the first '1'. You guys know a lot of languages, so please help me list more that it could run in ( :

EDIT: Added unary + at position 0 for Python to avoid the line being indented.

\$\endgroup\$
6
  • \$\begingroup\$ Spyder throws an IndentationError run in the command line. But maybe you didn't use the correct markdown syntax when posting the code here. \$\endgroup\$
    – Falko
    Commented Aug 19, 2014 at 13:45
  • \$\begingroup\$ As I feared the number of spaces turned out wrong, but now it is fixed. @Falko Each program should be all on one line so I don't think the missing spaces should cause an indentation error. Maybe your interpreter demands that everything be left aligned, or maybe the program for each number needs to be run separately instead of all together. \$\endgroup\$
    – feersum
    Commented Aug 19, 2014 at 15:21
  • \$\begingroup\$ My interpreter expects the first line to start at the very beginning. So your code can't be executed. \$\endgroup\$
    – Falko
    Commented Aug 19, 2014 at 15:50
  • \$\begingroup\$ I'm getting unexpected indent in the Python 2.7 console as well. But it works in Matlab so no worries. I believe it also works in Ruby. \$\endgroup\$ Commented Aug 19, 2014 at 15:51
  • \$\begingroup\$ Oops sorry Falko and Calvin's Hobbies, you are right it didn't work. But I've managed to circumvent the error by changing the first character to '+'. \$\endgroup\$
    – feersum
    Commented Aug 19, 2014 at 22:28
15
\$\begingroup\$

APL, 49

⌊⊃⍟○7⍟⍟1|/2111118 9⍝×-1 ×○3×4_5_×   1_   _⍝_⍝ __2

Altered programs

1  ⌊⊃⍟○7⍟⍟1|/2111118 9⍝×-1 ×○3×4_5_×   1_   _⍝_⍝ __2
2  ⌊⍟7⍟|21189×1×345× 1  ⍝⍝_2
3  ⌊○⍟/119-××5 1 ⍝ 2
4  ⌊7|18××4×1 ⍝2
5  ⌊⍟21×○5   
6  ⌊⍟19×51⍝2
7  ⌊11-4 ⍝
8  ⌊|8×× 2
9  ⌊/9×1 
10 ⌊2×5 
11 ⌊11 ⍝
12 ⌊1×12
13 ⌊13 
14 ⌊14⍝
15 ⌊15 
16 ⌊8×2

Explaination

I'll start from the bottom as it would make explaining easier

There are two language feature of APL to keep in mind. One, APL has no operator precedence, statements are always evaluated right to left. Two, many APL functions behave quite differently depending if it is given one argument on its right (monadic) or two arguments on its left and right (dyadic).

Monadic is round down (floor function), Dyadic × is obviously multiplication, comments out rest of the line
This should make these obvious:

16 ⌊8×2
15 ⌊15 
14 ⌊14⍝
13 ⌊13 
12 ⌊1×12
11 ⌊11 ⍝
10 ⌊2×5 

9: ⌊/9×1
/ is Reduce. Basically it takes the function of the left and the array on the right, insert the function between every pair of elements of the array and evaluate. (This is called "fold" in some languages)
Here, the right argument is a scalar so / does nothing.

8: ⌊|8×× 2
Monadic × is the signum function and monadic | is the absolute value function So, × 2 evaluates to 1 and |8×1 is of course 8

7: ⌊11-4 ⍝ should be obvious

6: ⌊⍟19×51⍝2
Monadic is natural log
So, ⍟19×51 evaluates to ln(19×51) = 6.87626..., and rounds it down to 6

5: ⌊⍟21×○5
Monadic multiplies its argument by π
⍟21×○5 is ln(21×5π) = 5.79869...

4: ⌊7|18××4×1 ⍝2
Dyadic | is the mod function
×4×1 evaluates to 1, and 7|18×1 is 18 mod 7 = 4

3: ⌊○⍟/119-××5 1 ⍝ 2
Space-separated values is an array. Note that in APL, when most scalar functions are give array arguments, it is an implicit map.
Dyadic is log
So ××5 1, is signum of signum on 5 and 1, which gives 1 1, 119-1 1 is ¯118 ¯118 (¯ is just the minus sign. APL has to distinguish between negative numbers and subtraction), and ⍟/¯118 ¯118 is log-118(-118) = 1

2: ⌊⍟7⍟|21189×1×345× 1 ⍝⍝_2
You can work it out yourself

1: ⌊⊃⍟○7⍟⍟1|/2111118 9⍝×-1 ×○3×4_5_× 1_ _⍝_⍝ __2
This one consist of a more complicated use of /. If n is a number, F is a function, and A is an array, then nF/A takes each group of n consecutive entries of A and apply F/. For example, 2×/1 2 3 takes each pair of consecutive entries (which are 1 2 and 2 3) and apply ×/ to each group to give 2 6
So, 1|/2111118 9 just returns 2111118 9 (as it applies |/ to scalars). Then, ⍟○7⍟⍟ applies ln, then log7 to those numbers, then multiplies them by π and ln again. The numbers that come out the other side are 1.46424... 0.23972...
Here, is just used to select the first element of an array.

\$\endgroup\$
0
9
\$\begingroup\$

CJam, 89 bytes

GiiAHH(5A5(7ii)ii;(;(-i((i(-i)i)ii)i()i((i(i(iiii(iii(iiiii(iiiii(i-i(iiiiiii(ii(ii(-ii-

This approach doesn't use any kind of comments.

i casts to integer, so it is a noop here. It could be replaced by whitespace, but the letters seem more readable to me...

Try it online by executing the following Code:

G,{)
"GiiAHH(5A5(7ii)ii;(;(-i((i(-i)i)ii)i()i((i(i(iiiii(iii(iiiii(iiiii(i-i(iiiiiii(ii(ii(-ii-"
%_~}%N*

Example run

$ cat nth-char.cjam 
G,{)"GiiAHH(5A5(7ii)ii;(;(-i((i(-i)i)ii)i()i((i(i(i
iii(iii(iiiii(iiiii(i-i(iiiiiii(ii(ii(-ii-"%_~}%N*N
$ cjam nth-char.cjam 
GiiAHH(5A5(7ii)ii;(;(-i((i(-i)i)ii)i()i((i(i(i
iii(iii(iiiii(iiiii(i-i(iiiiiii(ii(ii(-ii-
1
GiH(A(i)i((i((iii)(i(((
i(i(ii(ii(-(iii(ii(i-
2
GA(5ii(-(-ii(((iii(i(i(iii(((i
3
GHAii((ii(((iii(i-iii(-
4
GH(i(iii(i(i(i(ii-
5
G(i((i((i(((i((
6
G5)-ii(iii(i(
7
GAi(i(iiiii-
8
G5(-(i(ii(
9
G((i((((i
10
G7ii(i(i-
11
Gi((i(i(
12
Gi((ii(
13
G)i(i((
14
Giii(i
15
Giiiii
16
\$\endgroup\$
7
\$\begingroup\$

GolfScript, 61 bytes

1})_#;#;;00123358_(_};}_}_}} _}_(__6
_})4_)_+)__(__}__;_}___6

This takes advantage of comments (#) and the undocumented "super-comments" (everything following an unmatched } it is silently ignored).

_ is a noop. It could be replaced by whitespace, but the underscores seem more readable to me...

Try it online.

Example run

$ cat nth-char.gs
16,{)"1})_#;#;;00123358_(_};}_}_}} _}_(__6
_})4_)_+)__(__}__;_}___6"\%.~}%n*
$ golfscript nth-char.gs
1})_#;#;;00123358_(_};}_}_}} _}_(__6
_})4_)_+)__(__}__;_}___6
1
1)##;0238(}}}} }(_
}4)+_(__;}_6
2
1_#025(;}}}_
)))(};_6
3
1#;28}} (
4+(_}6
4
1;05}_}64)__6
5
1#2(}}
)(;6
6
1;3; 6)_}
7
1;8}(4(}
8
10(}
);
9
10}}4_6
10
11}_+_
11
12}
(6
12
13})_
13
13 )}
14
15})6
15
18((
16
\$\endgroup\$
5
+300
\$\begingroup\$

Zsh, 249 bytes

b
y
e
 
2y
y
yyyy
e
bye 1
e e
e
e 

9

 

 
1 

 


1

11


1


13



4



5



6


















b


b


b
y
y
y




e




 
 
 
6


8


1








0




b



b





y





e



e
 





7b
 

y



e


1 



5




2














b
b
yy

e

  
3
4

Verify all slices online!

Every sliced program is of the form:

[...junk]
bye N
[junk...]

For example, for \$ N = 8 \$:

b2y1e
 11
6

bye 8


y

 e
2
be4

Attempt This Online!

The only code that matters is the bye 8, which exits the program with the exit code of 8.

The rest of the junk is just ignored by zsh, because it's all invalid commands. (Everything after the first valid bye also doesn't matter, because it never gets executed).


To optimise this answer, we want to interleave the programs bye 1, b y e 2, b y e 3, etc., so that:

  • every program starts at an offset that is a multiple of its corresponding value of \$ N \$
  • none of the programs "overlap", putting different characters in the same place, which will break the code
  • the total length is minimised (of course)

This problem boils down to searching over the permutations of \$ [1, 16] \$, and then inserting each program in the chosen order at the first valid offset in the output code.

I tried various strategies to solve these constraints, including some obvious orderings picked by hand, and a greedy breadth-first search, but the most successful was a crude "Monte Carlo" method:

  1. shuffle the range into a particular order
  2. arrange the programs in that order
  3. if this solution was shorter than the previous iteration, then save it as the current shortest possibility
  4. repeat ad nauseam

Here's my optimiser code, in Python:

import re, random, time

def valid(code, n):
    match = pattern.search(''.join(code))
    return match and match[1] == str(n)

pattern = re.compile(r"^\s*bye\s+(\d+)\s*$", re.MULTILINE) 

# for repeatability
seed = int(time.time() * 1e6)
print(f"{seed=}")
random.seed(seed)

ns = list(range(1, 17))

# it's pretty much guaranteed we'll find a solution shorter than this fake one
result = "dummy" * 100

while True:
    code = ["\n"] * 1000
    random.shuffle(ns)
    used = set()
    for n in ns:
        used.add(n)
        out = code.copy()
        insert = f"bye {n}"
        offset = 0
        out[offset:offset + len(insert) * n:n] = insert
        while not all(valid(out[::i], i) for i in used):
            offset += n
            if offset >= 250:
                # too long
                break
            out = code.copy()
            out[offset:offset + len(insert) * n:n] = insert
        # hack to propogate break
        else:
            code = out
            continue
        break
    else:
        candidate = "".join(code).rstrip("\n")
        if len(candidate) < len(result):
            result = candidate
            print("------")
            print("length:", len(result))
            print(result)

Attempt This Online! (but due to ATO's timeout and non-streaming nature, this is probably better run on your actual computer)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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