views:

778

answers:

10

The Challenge

The shortest code by character count that takes a single input integer N (N >= 3) and returns an array of indices that when iterated would traverse an NxN matrix according to the JPEG "zigzag" scan pattern. The following is an example traversal over an 8x8 matrixsrc:

zigzag layout pattern

Examples

(The middle matrix is not part of the input or output, just a representation of the NxN matrix the input represents.)

                1 2 3
(Input) 3  -->  4 5 6  -->  1 2 4 7 5 3 6 8 9 (Output)
                7 8 9

                1  2  3  4
(Input) 4  -->  5  6  7  8  -->  1 2 5 9 6 3 4 7 10 13 14 11 8 12 15 16 (Output)
                9 10 11 12
               13 14 15 16

Notes

  • The resulting array's base should be appropriate for your language (e.g., Matlab arrays are 1-based, C++ arrays are 0-based).
  • This is related to this question.

Bonus

Extend your answer to take two inputs N and M (N, M >=3) and perform the same scan over an NxM matrix. (In this case N would be the number of columns and M the number of rows.)

Bonus Examples

                  1  2  3  4
(Input) 4 3  -->  5  6  7  8  -->  1 2 5 9 6 3 4 7 10 11 8 12 (Output)
                  9 10 11 12

                   1  2  3
(Input) 3 4  -->   4  5  6  -->  1 2 4 7 5 3 6 8 10 11 9 12 (Output)
                   7  8  9
                  10 11 12
+5  A: 

F#, 126 chars

let n=stdin.ReadLine()|>int
for i=0 to 2*n do for j in[id;List.rev].[i%2][0..i]do if i-j<n&&j<n then(i-j)*n+j|>printf"%d "

Examples:

$ echo 3 | fsi --exec Program.fsx
0 1 3 6 4 2 5 7 8

$ echo 4 | fsi --exec Program.fsx
0 1 4 8 5 2 3 6 9 12 13 10 7 11 14 15
Brian
`[x;y].[i%2]` rather than `if i%2=0 then x else y` - nice, gotta remember that one - props to @doublep
Brian
Oooh, that's a nice trick. I think that one might work in PowerShell as well.
Joey
+9  A: 

Python, 92, 95, 110, 111, 114, 120, 122, 162, 164 chars

N=input()
for a in sorted((p%N+p/N,(p%N,p/N)[(p%N-p/N)%2],p)for p in range(N*N)):print a[2],

Testing:

$ echo 3 | python ./code-golf.py 
0 1 3 6 4 2 5 7 8

$ echo 4 | python ./code-golf.py 
0 1 4 8 5 2 3 6 9 12 13 10 7 11 14 15

This solution easily generalizes for NxM boards: tweak the input processing and replace N*N with N*M:

N,M=map(int,raw_input().split())
for a in sorted((p%N+p/N,(p%N,p/N)[(p%N-p/N)%2],p)for p in range(N*M)):print a[2],

I suspect there's some easier/shorter way to read two numbers.

Testing:

$ echo 4 3 | python ./code-golf.py 
0 1 4 8 5 2 3 6 9 10 7 11
doublep
I'm not familiar with Python syntax, but how does this print the numbers separated by a space?
Adam
@Adam the comma does that, also "A '\n' character is written at the end, unless the print statement ends with a comma"
nevets1219
You can use 'split()' rather than 'replace()'
David
@David: No he can't. He'll be left with an array of two strings. He needs an array of two ints, so that's why he's using eval + replace. You could just use split and then cast each array element to an int, but I think that'll end up using more characters.
Wallacoloo
@wallacoloo: how about `N,M=input()` (python 2.x)?
Nas Banov
@EnTerr: This won't work for a space-separated input.
doublep
@doublep: Well yeah but nowhere is postulated the input should be in format '4\s+3'. Based on that i think any reasonable input requirement is ok - in this case '4,\s*3'. Why should we be biased towards Google Code Jam or C input style? Other languages (Python, Basic, ...) like CSVs better ;-)ps. example of imho unreasonable requirement - 'encode m and n by interlacing the bits and input that, e.g. for 4 (0b100) and 3 (0b011) type 37 (0b100101)
Nas Banov
+9  A: 

Ruby, 69 89 chars

n=gets.to_i
puts (0...n*n).sort_by{|p|[t=p%n+p/n,[p%n,p/n][t%2]]}*' '

89 chars

n=gets.to_i
puts (0...n*n).map{|p|[t=p%n+p/n,[p%n,p/n][t%2],p]}.sort.map{|i|i[2]}.join' '

Run

> zigzag.rb
3
0 1 3 6 4 2 5 7 8

> zigzag.rb
4
0 1 4 8 5 2 3 6 9 12 13 10 7 11 14 15

Credits to doublep for the sort method.

Adam
`n=eval *$<;p (0...n*n).sort_by{|p|[t=p%n+p/n,[p%n,p/n][t%2]]}`61
matyr
@matyr: `p` prints `[0, 1, 3,...]` instead of `0 1 3...`.
J.F. Sebastian
@matyr, thanks for that. made it 69. eval *$< didn't work on my system.
Adam
@Sebastian "an array of indices" doesn't indicate space-separated.@Adam `puts ` -> `$><<`.
matyr
+3  A: 

MATLAB, 101/116 chars

Its basically a condensed version of the same answer given here, to be run directly on the command prompt:

N=input('');i=fliplr(spdiags(fliplr(reshape(1:N*N,N,N)')));i(:,1:2:end)=flipud(i(:,1:2:end));i(i~=0)'

and an extended one that read two values from the user:

S=str2num(input('','s'));i=fliplr(spdiags(fliplr(reshape(1:prod(S),S)')));i(:,1:2:end)=flipud(i(:,1:2:end));i(i~=0)'

Testing:

3
ans =
     1     2     4     7     5     3     6     8     9

and

4 3
ans =
     1     2     5     9     6     3     4     7    10    11     8    12
Amro
+2  A: 

Ruby 137 130 138 characters

n=gets.to_i
def g(a,b,r,t,s);x=[s*r]*t;t==r ?[a,x,a]:[a,x,g(b,a,r,t+1,-s),x,a];end
q=0;puts ([1]+g(1,n,n-1,1,1)).flatten.map{|s|q+=s}*' '

$ zz.rb
3
1 2 4 7 5 3 6 8 9

$ zz.rb
4
1 2 5 9 6 3 4 7 10 13 14 11 8 12 15 16
Anurag
You should use `puts [array]*' '` to match the output in the question.
Adam
+15  A: 

J, 13 15 characters

;<@|.`</.i.2$

Usage:

   ;<@|.`</.i.2$  3
0 1 3 6 4 2 5 7 8

   ;<@|.`</.i.2$  4
0 1 4 8 5 2 3 6 9 12 13 10 7 11 14 15

Explanation

(NB. is J's comment indicator)

;         NB. Link together...
<@|.`<    NB. ... 'take the reverse of' and 'take normally'
/.        NB. ... applied to alternating diagonals of...
i.        NB. ... successive integers starting at 0 and counting up to fill an array with dimensions of...
2$        NB. ... the input extended cyclically to a list of length two.

J, bonus, 13 characters

;<@|.`</.i.|.

Usage:

   ;<@|.`</.i.|. 3 4
0 1 3 6 4 2 5 7 9 10 8 11

   ;<@|.`</.i.|. 9 6
0 1 9 18 10 2 3 11 19 27 36 28 20 12 4 5 13 21 29 37 45 46 38 30 22 14 6 7 15 23 31 39 47 48 40 32 24 16 8 17 25 33 41 49 50 42 34 26 35 43 51 52 44 53
David
that's just insane..
Blindy
I like how it takes two characters to do 'applied to alternating diagonals of'.
Cam
yea the 'alternating diagonal' built-in is what makes this possible. im workin on a goflscript solution which doesnt use that.. will see how short it can be
Claudiu
+4  A: 

Golfscript, 26/30 32/36 45 59 characters

Shortest non-J solution so far:

Updated sort (don't tell the others!) - 30 chars:

 ~:1.*,{..1/\1%+.2%.+(@*]}$' '* #solution 1
#~:\.*,{.\/1$\%+.1&@.~if]}$' '* #solution 2
#~\:1*,{..1/\1%+.2%.+(@*]}$' '* #(bonus)
#~\:\*,{.\/1$\%+.1&@.~if]}$' '* #(bonus)

Straight implementation - 36 chars:

 ~:@.*,{[.@%:|\@/:^+|^- 2%^|if]}$' '*
#~\:@*,{[.@%:|\@/:^+|^- 2%^|if]}$' '* #(bonus)

If you can provide output as "013642578" instead of "0 1 3 6 4 2 5 7 8", then you can remove the last 4 characters.

Credit to doublep for the sorting technique.


Explanation:

~\:@*        #read input, store first number into @, multiply the two
,            #put range(@^2) on the stack
{...}$       #sort array using the key in ...
" "*         #join array w/ spaces

and for the key:

[            #put into an array whatever is left on the stack until ]
.@%:|        #store @%n on the stack, also save it as |
\@/:^        #store @/n on the stack, also save it as ^
+            #add them together. this remains on the stack.
|^- 2%^|if   #if (| - ^) % 2 == 1, then put ^ on stack, else put | on stack.
]            #collect them into an array
Claudiu
@editor: ah nice way of removing spaces =)
Claudiu
@Nabb: you sure the updated sort works? i tried it but it gives different output
Claudiu
@Claudiu: output is exactly the same for both the square and bonus problems for the values I tested
Nabb
Not familiar with Golfscript, but `(a+b)%2` is equal to `(a-b)%2`. You might be able to %2 the sum of `|` and `^` if it helps saving some characters.
Adam
+1  A: 

Haskell 117 characters

i s=concatMap(\q->d q++(reverse.d$q+1))[0,2..s+s]
 where d r=[x+s*(r-x)|x<-[0..r],x<s&&(r-x)<s]
main=readLn>>=print.i

Runs:

$ echo 3 | ./Diagonals 
[0,1,3,6,4,2,5,7,8]

$ echo 4 | ./Diagonals 
[0,1,4,8,5,2,3,6,9,12,13,10,7,11,14,15]

The rectangular variant is a little longer, at 120 characters:

j(w,h)=concatMap(\q->d q++(reverse.d$q+1))[0,2..w+h]
 where d r=[x+w*(r-x)|x<-[0..r],x<w&&(r-x)<h]
main=readLn>>=print.j

Input here requires a tuple:

$ echo '(4,3)' | ./Diagonals 
[0,1,4,8,5,2,3,6,9,10,7,11]

$ echo '(3,4)' | ./Diagonals 
[0,1,3,6,4,2,5,7,9,10,8,11]

Answers are all 0-based, and returned as lists (natural forms for Haskell).

MtnViewMark
+1  A: 

Perl 102 characters

<>=~/ /;print map{$_%1e6," "}sort map{($x=($%=$_%$`)+($/=int$_/$`))*1e9+($x%2?$/:$%)*1e6+$_}0..$'*$`-1

usage :

echo 3 4 | perl zigzag.pl
0 1 3 6 4 2 5 7 9 10 8 11
M42
A: 

C89 (280 bytes)

I guess this can still be optimized - I use four arrays to store the possible movement vectors when hitting a wall. I guess it can be done, saving some chars at the definition, but I think it will cost more to implement the logic further down. Anyway, here you go:

t,l,b,r,i,v,n;main(int c,char**a){n=atoi(*++a);b=n%2;int T[]={n-1,1},L[]={1-n,n}
,B[]={1-n,1},R[]={n-1,n};for(c=n*n;c--;){printf("%d%c",i,c?32:10);if(i>=n*(n-1))
v=B[b=!b];else if(i%n>n-2){if(!(n%2)&&i<n)goto g;v=R[r=!r];}else if(i<n)g:v=T[t=
!t];else if(!(i%n))v=L[l=!l];i+=v;}}

compiles with a few warnings, but as far as I know it is portable C89. I'm actually not sure whether my algorithm is clever at all, maybe you can get way shorter with a better one (haven't taken the time to understand the other solutions yet).