Peter’s blog ✴ Week 101 ✴ 22 February 2021
THE WEEKLY CHALLENGE
Spiral triangle
You are given an array @A of items (integers say, but they can be anything).
Your task is to pack that array into an MxN matrix spirally counterclockwise, as tightly as possible.
‘Tightly’ means the absolute value |M-N| of the difference has to be as small as possible.
Input: @A = (1,2,3,4) Output: 4 3 1 2
Since the given array is already a 1x4 matrix on its own, but that's not as tight as possible. Instead, you'd spiral it counterclockwise. 4 3 1 2 Example 2: Input: @A = (1..6) Output: 6 5 4 1 2 3 or 5 4 6 3 1 2
Either will do as an answer, because they're equally tight. Example 3: Input: @A = (1..12) Output: 9 8 7 6 10 11 12 5 1 2 3 4 or 8 7 6 9 12 5 10 11 4 1 2 3
An interesting challenge, and my attempt at a reasonably compact solution.
First let's get the closest width and height. I start with the
integer part of the square root, $j, of $count, the number of items
to be displayed.
I then decrement $j until $count / $j is integral and set these
two values as the width and height of the output matrix, being the
closest pair of numbers whose product is $count. I made the
larger one the height to maximise the size of matrix I could easily display
in this blog, but swapping the width and height is equally valid.
Now I populate the matrix. The array pointed to by $steps contains the
four possible $x and $y differences possible from one point to the next.
The first point is required to be at (0, $height - 1), using
the convention that $y increases downwards and $x increases
to the right. The variable $s points to the pair of changes in
@sets to be applied to ($x, $y) to get to the next point.
However, before using that next point, I check that it is not
outside the bounds of the matrix and not already occupied. If that
test fails, I increment $s (modulo 4) and thus get a new
pair of increments from $steps - or in other words, the snail
that is planting the numbers in the matrix turns left.
This algorithm will always find the next position to be filled, and since it is contained within this loop:
for $j (0 .. $width * $height - 1)
it will run out of blank positions exactly when the matrix is complete.
This is quite a complicated explanation of some quite dense coding, but I believe it is an efficient and scalable solution.
#!/usr/bin/perl # Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge use v5.26; # The Weekly Challenge - 2021-02-22 use utf8; # Week 101 - task 1 - Pack a spiral use warnings; # Peter Campbell Smith binmode STDOUT, ':utf8'; use Encode; pack_a_spiral(1 .. 100); pack_a_spiral(15 .. 21); pack_a_spiral(951 .. 1049); pack_a_spiral('aa' .. 'xw'); my @x; push @x, 2 ** $_ for 0 .. 31; pack_a_spiral(@x); sub pack_a_spiral { my (@z, $w, $h, $count, $width, $height, $diff, $x, $y, $j, @factors, $steps, @matrix, @steps, $s, @try, $cw); # initialise @z = @_; $count = @z; # get closest factors of $count for ($j = int(sqrt($count));; $j --) { if ($count / $j == int($count / $j)) { ($width, $height) = ($j, int($count / $j)); last; } } # initialise values for spiral $steps = [[1, 0], [0, -1], [-1, 0], [0, 1]]; $s = 0; ($x, $y) = (0, $height - 1); $cw = 0; # create spiral for $j (0 .. $width * $height - 1) { $matrix[$x][$y] = $z[$j]; $cw = length($z[$j]) if length($z[$j]) > $cw; @try = ($x + $steps->[$s]->[0], $y + $steps->[$s]->[1]); # have we reached a corner? if ($try[0] < 0 or $try[0] >= $width or $try[1] < 0 or $try[1] >= $height or defined $matrix[$try[0]][$try[1]]) { $s = ($s + 1) % 4; } ($x, $y) = ($x + $steps->[$s]->[0], $y + $steps->[$s]->[1]); } $cw ++; # display say qq[\nInput: $count items - $z[0] to $z[-1]]; say qq[Output: dimensions $width × $height]; for $y (0 .. $height - 1) { for $x (0 .. $width - 1) { printf (qq[%${cw}s], $matrix[$x][$y]); } say ''; } }
29 lines of code
Completed after the closing date and not submitted to GitHub
Input: 100 items - 1 to 100 Output: dimensions 10 × 10 28 27 26 25 24 23 22 21 20 19 29 58 57 56 55 54 53 52 51 18 30 59 80 79 78 77 76 75 50 17 31 60 81 94 93 92 91 74 49 16 32 61 82 95 100 99 90 73 48 15 33 62 83 96 97 98 89 72 47 14 34 63 84 85 86 87 88 71 46 13 35 64 65 66 67 68 69 70 45 12 36 37 38 39 40 41 42 43 44 11 1 2 3 4 5 6 7 8 9 10 Input: 7 items - 15 to 21 Output: dimensions 1 × 7 21 20 19 18 17 16 15 Input: 99 items - 951 to 1049 Output: dimensions 9 × 11 977 976 975 974 973 972 971 970 969 978 1007 1006 1005 1004 1003 1002 1001 968 979 1008 1029 1028 1027 1026 1025 1000 967 980 1009 1030 1043 1042 1041 1024 999 966 981 1010 1031 1044 1049 1040 1023 998 965 982 1011 1032 1045 1048 1039 1022 997 964 983 1012 1033 1046 1047 1038 1021 996 963 984 1013 1034 1035 1036 1037 1020 995 962 985 1014 1015 1016 1017 1018 1019 994 961 986 987 988 989 990 991 992 993 960 951 952 953 954 955 956 957 958 959 Input: 621 items - aa to xw Output: dimensions 23 × 27 cs cr cq cp co cn cm cl ck cj ci ch cg cf ce cd cc cb ca bz by bx bw ct ge gd gc gb ga fz fy fx fw fv fu ft fs fr fq fp fo fn fm fl fk bv cu gf ji jh jg jf je jd jc jb ja iz iy ix iw iv iu it is ir iq fj bu cv gg jj me md mc mb ma lz ly lx lw lv lu lt ls lr lq lp lo ip fi bt cw gh jk mf os or oq op oo on om ol ok oj oi oh og of oe ln io fh bs cx gi jl mg ot qy qx qw qv qu qt qs qr qq qp qo qn qm od lm in fg br cy gj jm mh ou qz sw sv su st ss sr sq sp so sn sm ql oc ll im ff bq cz gk jn mi ov ra sx um ul uk uj ui uh ug uf ue sl qk ob lk il fe bp da gl jo mj ow rb sy un vu vt vs vr vq vp vo ud sk qj oa lj ik fd bo db gm jp mk ox rc sz uo vv wu wt ws wr wq vn uc sj qi nz li ij fc bn dc gn jq ml oy rd ta up vw wv xm xl xk wp vm ub si qh ny lh ii fb bm dd go jr mm oz re tb uq vx ww xn xw xj wo vl ua sh qg nx lg ih fa bl de gp js mn pa rf tc ur vy wx xo xv xi wn vk tz sg qf nw lf ig ez bk df gq jt mo pb rg td us vz wy xp xu xh wm vj ty sf qe nv le if ey bj dg gr ju mp pc rh te ut wa wz xq xt xg wl vi tx se qd nu ld ie ex bi dh gs jv mq pd ri tf uu wb xa xr xs xf wk vh tw sd qc nt lc id ew bh di gt jw mr pe rj tg uv wc xb xc xd xe wj vg tv sc qb ns lb ic ev bg dj gu jx ms pf rk th uw wd we wf wg wh wi vf tu sb qa nr la ib eu bf dk gv jy mt pg rl ti ux uy uz va vb vc vd ve tt sa pz nq kz ia et be dl gw jz mu ph rm tj tk tl tm tn to tp tq tr ts rz py np ky hz es bd dm gx ka mv pi rn ro rp rq rr rs rt ru rv rw rx ry px no kx hy er bc dn gy kb mw pj pk pl pm pn po pp pq pr ps pt pu pv pw nn kw hx eq bb do gz kc mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm kv hw ep ba dp ha kd ke kf kg kh ki kj kk kl km kn ko kp kq kr ks kt ku hv eo az dq hb hc hd he hf hg hh hi hj hk hl hm hn ho hp hq hr hs ht hu en ay dr ds dt du dv dw dx dy dz ea eb ec ed ee ef eg eh ei ej ek el em ax aa ab ac ad ae af ag ah ai aj ak al am an ao ap aq ar as at au av aw Input: 32 items - 1 to 2147483648 Output: dimensions 4 × 8 8192 4096 2048 1024 16384 134217728 67108864 512 32768 268435456 33554432 256 65536 536870912 16777216 128 131072 1073741824 8388608 64 262144 2147483648 4194304 32 524288 1048576 2097152 16 1 2 4 8
Any content of this website which has been created by Peter Campbell Smith is in the public domain