Fourth grade math homework for the week: A most inefficient traveling salesman

10

My daughter had the following assignment for her math homework. Imagine six friends living on a line, named E, F, G, H, J and K. Their positions on the line are as indicated (not to scale) below:

Thus, F lives five units from E, and two units from G, and so forth.

Your assignment: craft a program that identifies a path that visits each friend exactly once with a total length of n units, taking the locations of the friends and n as inputs. It should report the path if it finds it (for example, for length 17 it might report "E,F,G,H,J,K", and it should exit gracefully if no solution exists. For what it's worth, I completed an ungolfed solution in Mathematica in 271 bytes. I suspect it's possible much more concisely than that.

Michael Stern

Posted 2015-02-19T01:33:02.933

Reputation: 3 029

3This might be better as a program that takes inputs (ex. [0, 5, 7, 13, 16, 17] and 62) so that you can make sure it's not specifically hard-coded to this case. – Doorknob – 2015-02-19T01:42:17.410

@Doorknob, good point. I have adjusted the assignment accordingly. – Michael Stern – 2015-02-19T01:56:53.213

I think @Doorknob 's idea was that you make the specification for the general case (position of friends may vary) and use the specific case as an example (to avoid answers hardcoded to the specific case). Maybe you agree with him, but it's not entirely clear from the current edit. Also, should the path found be exactly n units as specified by the user? Or the maximum possible? What was the intention? – Level River St – 2015-02-19T02:05:06.153

1Does the path start at any friend? – xnor – 2015-02-19T02:23:04.157

1Can I define the format of the input and output strings? Is an input like "[0, 5, 7, 13, 16, 17], 62" and an output "(7, 16, 0, 17, 5, 13)" ok? – Logic Knight – 2015-02-19T06:15:16.173

answering various questions above: (1) you can start at any friend. Given the six friends above counting reflected paths, there are 720 possible paths. (2) assume the locations of the friends and the target distance are provided at runtime. For every length where a solution is possible, at least two solutions are possible (because of reflection). You need to produce only one. If the requested length is impossible, exit gracefully. – Michael Stern – 2015-02-19T11:23:00.967

1@Geobits just sloppiness on my part. Corrected. – Michael Stern – 2015-02-19T16:07:39.087

Answers

1

J, 54 bytes

Outputs one correct route. If no route exists it outputs nothing.

   f=.4 :'{.(x=+/|:2|@-/\"#.s A.y)#(s=.i.!6)A.''EFGHJK'''

   62 f 0 5 7 13 16 17
GJEKFH

52-byte code that outputs all routes (one per line):

f=.4 :'(x=+/|:2|@-/\"#.s A.y)#(s=.i.!6)A.''EFGHJK'''

38-byte code that outputs positions instead letters:

f=.4 :'p#~x=+/|:2|@-/\"#.p=.(i.!6)A.y'

randomra

Posted 2015-02-19T01:33:02.933

Reputation: 19 909

I can't vet the code, but per your summary of it, this seems to be the shortest entry that does everything the problem requires. – Michael Stern – 2015-02-23T17:34:53.967

6

Mathematica, 55 or 90 bytes

Mathematica you said? ;)

FirstCase[Permutations@#,p_/;Tr@Abs@Differences@p==#2]&

This is an anonymous function that first takes the positions of the friends (in any order), and then the target length. It returns Missing[NotFound], if no such path exists.

FirstCase[Permutations@#,p_/;Tr@Abs@Differences@p==#2]&[{0, 5, 7, 13, 16, 17}, 62]
(* {7, 16, 0, 17, 5, 13} *)

I can save four bytes if returning all valid paths is allowed (FirstCase -> Cases).

Returning an array of strings is a bit more cumbersome:

FromCharacterCode[68+#]&/@Ordering@FirstCase[Permutations@#,p_/;Tr@Abs@Differences@p==#2]&

Martin Ender

Posted 2015-02-19T01:33:02.933

Reputation: 184 808

Could you adjust so that it responds with the letters rather than just the locations? – Michael Stern – 2015-02-19T14:39:42.300

@MichaelStern It's not really clear from the question how much should be hardcoded and how much should be part of the parameters? Should the input be something like a mapping from letters to positions? – Martin Ender – 2015-02-19T15:02:12.210

Assume the letters are always in the order given in the number line above (E, F, G, H, J, K). The distances between them should be passed to the function as you do in your solution. – Michael Stern – 2015-02-19T16:04:46.070

@MichaelStern I added a version that returns an array of strings. It supports any number of positions in the list, but after Z will continue with the next ASCII characters (not that you want to run my code for n > 20 anyway :D). – Martin Ender – 2015-02-19T19:49:13.103

5

Python 2, 154 148 bytes

(or 118 bytes for the general solution)

This program accepts a line with a list and an integer like '[0, 5, 7, 13, 16, 17], n' on stdin and prints a path on the output of length n or nothing if impossible.

# echo "[0, 5, 7, 13, 16, 17], 62" | python soln.py 
['G', 'J', 'E', 'K', 'F', 'H']

It is challenging to write small programs in Python that require permutations. That import and use is very costly.

from itertools import*
a,c=input()
for b in permutations(a):
 if sum(abs(p-q)for p,q in zip(b[1:],b))==c:print['EFGHJK'[a.index(n)]for n in b];break

The source for the OP requirement before the minifier:

from itertools import*

puzzle, goal = input()
for option in permutations(puzzle):
    if sum(abs(p-q) for p,q in zip(option[1:], option)) == goal :
        print ['EFGHJK'[puzzle.index(n)] for n in option];
        break

The general solution (not minified):

from itertools import*

puzzle, goal = input()
for option in permutations(puzzle):
    if sum(abs(p-q) for p,q in zip(option[1:], option)) == goal :
        print option;
        break

Due to the simple algorithm and vast number of combinations, execution for more than 20 initial positions will be very slow.

Logic Knight

Posted 2015-02-19T01:33:02.933

Reputation: 6 622

You could save a few bytes with from itertools import*. Also, Python 3 might be shorter with input() and *a,c=map(...) if it can work with the rest of your program. – grc – 2015-02-19T06:08:34.650

Thanks for the import tip. I am resisting a py3 install and conversion of my code base. I am waiting until every 3rd party module I use is available and stable under py3 (I use many old and obscure ones). – Logic Knight – 2015-02-19T06:21:03.940

Could you adjust so that it responds with the letters rather than just the locations? – Michael Stern – 2015-02-19T14:38:26.433

chr(a.index(n)+69)? – Martin Ender – 2015-02-20T19:52:48.187

Nice optimisation. But I think @MichaelStern really wants to see the 'EFGHJK', and it was easy enough so I wrote the code that way. – Logic Knight – 2015-02-21T11:40:55.807

4

J (48 or 65)

I hypothesize that this can be golfed a hell of a lot more. Feel free to use this as a jumping off point to golf it further

]A.~[:I.(=([:([:+/}:([:|-)}.)"1(A.~([:i.[:!#))))

Or with letters:

([:I.(=([:([:+/}:([:|-)}.)"1(A.~([:i.[:!#)))))A.[:(a.{~65+[:i.#)]

What it does:

   62 (]A.~[:I.(=([:([:+/}:([:|-)}.)"1(A.~([:i.[:!#))))) 0 5 7 13 16 17
 7 16  0 17  5 13
 7 16  5 17  0 13
 7 17  0 16  5 13
 7 17  5 16  0 13
13  0 16  5 17  7
13  0 17  5 16  7
13  5 16  0 17  7
13  5 17  0 16  7

(I hope this I/O format is okay...)

How it does it:

(A.~([:i.[:!#))

Generates all permutations of the input

([:+/}:([:|-)}.)"1

Calculates the distance

(]A.~[: I. (= ([:distance perms)))

Sees which results are the same as the input, and re-generates those permutations (I suspect some characters can be shaved off here)

With letters:

((a.{~65+[:i.#))

Create a list of the first n letters, where n is the length of the input list

indices A. [: letters ]

does the same as above

ɐɔıʇǝɥʇuʎs

Posted 2015-02-19T01:33:02.933

Reputation: 4 449

Can you adjust it to report the answer in terms of letters? – Michael Stern – 2015-02-19T18:17:16.390

@MichaelStern I could, but that would add quite a bit to the character count (J is terrible with strings). I'll try it now, to see what the damage might be. – ɐɔıʇǝɥʇuʎs – 2015-02-19T18:18:58.763

3

Octave, 73

function r=t(l,d,s)r=perms(l)(find(sum(abs(diff(perms(d)')))==s,1),:);end

There's really no un-golfing this, so let me try to explain... from inside to out, we permute all the distances, then for each permutation, we take the differences between houses, take the absolute value as a distance, add them up, find the index of the first permutation with the desired distance, and permute the letters and find that particular permutation of letters.

octave:15> t(["E" "F" "G" "H" "J" "K"],[0 5 7 13 16 17],62)
ans = HEJFKG

which is 13-0-16-5-17-7 => 13+16+11+12+10=62.

octave:16> t(["E" "F" "G" "H" "J" "K"],[0 5 7 13 16 17],2)
ans = 

(blank for impossible inputs)

dcsohl

Posted 2015-02-19T01:33:02.933

Reputation: 641

I don't know what the deal is, but perms() in Octave 3.6.2 on ideone.com is having issues with the vector of strings. – Alex A. – 2015-02-20T17:20:34.950

Interesting. I have 3.8.1 locally. – dcsohl – 2015-02-20T21:26:17.310

2

Matlab (86)

x=input('');X=perms(1:6);disp(char(X(find(sum(abs(diff(x(X).')))==input(''),1),:)+64))

Example in which a solution exists:

>> x=input('');X=perms(1:6);disp(char(X(find(sum(abs(diff(x(X).')))==input(''),1),:)+64))
[0, 5, 7, 13, 16, 17]
62
DBFAEC
>>

Example in which a solution doesn't exist:

>> x=input('');X=perms(1:6);disp(char(X(find(sum(abs(diff(x(X).')))==input(''),1),:)+64))
[0, 5, 7, 13, 16, 17]
100
>> 

Matlab (62)

If the output format can be relaxed by producing positions instead of letters, and producing an empty matrix if no solution exists:

X=perms(input(''));X(find(sum(abs(diff(X.')))==input(''),1),:)

Example in which a solution exists:

>> X=perms(input(''));X(find(sum(abs(diff(X.')))==input(''),1),:)
[0, 5, 7, 13, 16, 17]
62
ans =
    13     5    17     0    16     7

Example in which a solution doesn't exist:

>> X=perms(input(''));X(find(sum(abs(diff(X.')))==input(''),1),:)
[0, 5, 7, 13, 16, 17]
62
ans =
   Empty matrix: 0-by-6

Matlab (54)

If it's acceptable for the program to provide all valid paths:

X=perms(input(''));X(sum(abs(diff(X.')))==input(''),:)

Example in which a solution exists:

>> X=perms(input(''));X(sum(abs(diff(X.')))==input(''),:)
[0, 5, 7, 13, 16, 17]
62
ans =
    13     5    17     0    16     7
    13     5    16     0    17     7
    13     0    17     5    16     7
    13     0    16     5    17     7
     7    16     5    17     0    13
     7    16     0    17     5    13
     7    17     5    16     0    13
     7    17     0    16     5    13

Luis Mendo

Posted 2015-02-19T01:33:02.933

Reputation: 87 464

1

Haskell, 109 bytes

import Data.List
a%b=abs$snd a-snd b
n#l=[map(fst)p|p<-permutations(zip['E'..]l),n==sum(zipWith(%)p(tail p))]

Usage example: 17 # [0, 5, 7, 13, 16, 17] which outputs all valid paths, i.e. ["EFGHIJ","JIHGFE"]. If there's no valid path, the empty list [] is returned.

The list of letters includes I (hope that's fine).

How it works: make a list of (name, position) pairs, permute and take those where the path length equals n and remove the position part.

nimi

Posted 2015-02-19T01:33:02.933

Reputation: 34 639