12
\$\begingroup\$

Your task is, given a square grid of digits (0-9), output one of the ways that the digits can be grouped such that:

  1. Each digit is part of exactly one group
  2. All groups have the same number of digits
  3. All groups are bounded by one polygon-like shape (this means that every digit in the group is next to [left, right, up, down] at least one other digit of the same group, unless each group has 1 element).
  4. All groups have the same sum

The input grid will always be a square: You may choose any input method you would like (including supplying arguments to a function or method). In addition, the input will supply the number of groups that your program should group the digits into.

Example input:

Suppose your input format is stringOfDigits numberOfGroups.

An example input would be:

156790809 3

which would translate to (a grid of sqrt(9) * sqrt(9))

1 5 6
7 9 0
8 0 9

which you would have to divide into 3 groups, each of which should have 9 / 3 = 3 elements with the same sum.

Output: Output should be the string of digits, with optional spaces and newlines for formatting, with each digit followed by a letter a-z indicating its group. There should be exactly numberOfTotalDigits / numberOfGroups elements in each group. You will never have to divide something into more than 26 groups.

Example output:

1a 5a 6b
7c 9a 0b
8c 0c 9b

Note that replacing all as with bs and bs with as is equally valid. As long as each group is denoted by a distinct letter, the output is valid.

In addition, I expect most programs to output something along the lines of this, because newlines/spaces are optional:

1a5a6b7c9a0b8c0c9b

In this case, adding all digits of group a, b, or c makes 15. In addition, all groups are bound by some polygon.

Invalid outputs:

1a 5a 6b
7c 9a 0c
8c 0b 9b

because the groups do not form polygons (specifically, the 6b is isolated and 0c is also lonely).

1a 5a 6b
7c 9a 0b
8c 0b 9b

because the group b has 4 elements while c only has 2.

Etc.

If there is no valid solution, your program may do anything (i.e. stop, crash, run forever) but if your program prints None when there is no valid solution, -15 to your score.

If there is more than one solution, you only have to print one, but -20 if your program prints all of them separated by some delimiter.

This is code golf, so shortest code (with bonuses) wins!

\$\endgroup\$
  • \$\begingroup\$ In the first invalid output, I think you mean the 6b is isolated, not the 0b. \$\endgroup\$ – Level River St Nov 9 '14 at 1:33
  • \$\begingroup\$ Does it matter how fast our program is? What about if it's too slow to validate if it works? \$\endgroup\$ – Beta Decay Nov 9 '14 at 8:29
  • \$\begingroup\$ 156790889 3 seems like it should be 156790809 3 \$\endgroup\$ – isaacg Nov 9 '14 at 9:35
10
\$\begingroup\$

Pyth, 122 - 20 - 15 = 87

=Z/lzQ=ks^lz.5Jm]dUzL[-bk+bk?tb%bkb?hb%hbkb)FNJIgNZB~Jm+NksmybN;|jbS{msm+@zk@S*Z<GQxsdkUzfqSsTUz^fqsmv@*ZzbY/smvdzQJQ"None

Changes:

  • 130 -> 120: Switched to newline separated input.

  • 120 -> 134: Fixed a bug involving groups not of size equal to the side length of the matrix.

  • 134 -> 120: Prints all solutions, including ones equivalent under group renaming.

  • 120 -> 122: Fixed a bug where only paths would be generated, instead of all legal groups.

Test run:

pyth programs/sum_group.pyth <<< '156790809
3'
1a5a6b7c9a0b8c0c9b
1a5a6c7b9a0c8b0b9c
1b5b6a7c9b0a8c0c9a
1b5b6c7a9b0c8a0a9c
1c5c6a7b9c0a8b0b9a
1c5c6b7a9c0b8a0a9b


pyth programs/sum_group.pyth <<< '156790808
3'
None

pyth programs/sum_group.pyth <<< '1111     
2'
1a1a1b1b
1a1b1a1b
1b1a1b1a
1b1b1a1a

Explanation:

Pyth code           (Pseudo)-Python code              Comments

(implicit)          z = input()                       z is the digit string
(implicit)          Q = eval(input())                 S is the number of groups
(implicit)          G = 'abcdefghijklmnopqrstuvwxyz'
=Z/lzQ              Z = len(z)/Q                      Z is the size of each group.
=ks^lz.5            k = int(len(z) ** .5)             k is the side length of the matrix.
Jm]dUz              J = map(lambda d:[d], range(len(z))) Locations are encoded as numbers.
L                   def y(b): return                  y will be the transition function.
 [-bQ                         [b-k,                   Move up - the row above is k less.
  +bQ                          b+k,                   Move down - the row below is k more.
  ?tb%bkb                      b-1 if b%k else b      Move left, unless at the left edge.
  ?hb%hbkb)                    b+1 if (b+1)%k else b] Move right, unless at right edge.
FNJ                 for N in J:                       This constructs the list of all
   IgNZB                       if N[Z-1]: break       Z-length connected groups.
   ~Jm+Nk                      J+=map(lambda k: N+[k],  Append to J the group of N +
      smybN                          sum(map(lambda b:  anything reachable from
                                     y(b),N)))        anywhere in N.
   ;                (end for)
|                   or                                Print first truthy thing between
 S{                 sorted(set(                       Unique elements in sorted order of
   ms               map(lambda b:sum(                 Map+sum over allowable combinations
     m+@zd          map(lambda d:z[d]+                Character in original digit string
       @S*Z<GQ      sorted(G[:Q]*Z)[                  Repeated and sorted early alphabet
        xsbd        sum(b).index(d)],                 At index of number in sum of groups
      Uz                range(len(z)))                Over possible indexes.
   f                filter(lambda T:                  To generate allowable combinations, 
                                                      we will filter all groups of Q paths.
    qSsTUz          sorted(sum(T)) == range(len(z))   Ensure all locations are visited.
    ^                                                 Combinations of
     f              filter(lambda Y:                  Filter over connected Z-length groups
      qsm           equal(sum(map(lambda k:           Sum of the values of the group
         v@*ZzkY    eval((z*Z)[k]),Y)                 In the original digit string
       /smvbzQ      sum(map(lambda b:eval(b),z))/Q    must equal the sum of all values in z
                                                      divided by the number of groups.
      J             J                                 Filter over connected Z-length groups
     Q              Q                                 Combinations of length Q
 "None              "None"                            If the above was empty, print "None"
\$\endgroup\$
  • 9
    \$\begingroup\$ "Pyth"? You misspelled "base64". \$\endgroup\$ – Ingo Bürk Nov 9 '14 at 13:40
4
\$\begingroup\$

JavaScript (ES6) 361 (376-15) 372

(Maybe can still be golfed a little more)

As a function, first param is the string of digits and second param is the number of groups.
It's a naive recursive search, stopping at first solution found (no -20 bonus).
Need some more test cases to verify performance on some bigger input.

F=(g,n,l=g.length,i=w=Math.sqrt(l),o=s=h='',
  R=(g,p,k,j=l/n,t=s/n,v=0,h=String.fromCharCode(97+k))=>(
    t-=g[p],!(t<0)&&(
      g=[...g],g[p]=h,
      S=f=>g.some((c,p)=>c<':'&&f(p)),
      --j?S(p=>(g[p+1]==h|g[p-1]==h|g[p+w+1]==h|g[p-w-1]==h)?v=R(g,p,k,j,t):0)
      :t?0:k?S(p=>v=R(g,p,k-1)):v=g
    ),v
  )
)=>([for(c of g)(s-=-c,h+=--i?c:(i=w,c+':'))],h=R(g=h,-1,n,1))?h.map((c,p)=>o+=c!=':'?g[p]+c:'')&&o:'None'

Ungolfed & Explained

F=(g,n)=> 
{
  var l = g.length, // string size, group size is l/n 
      w = Math.sqrt(l), // width of grid
      s,i,h,o;

  // Build a new string in h, adding rows delimiters that will act as boundary markers  
  // At the same time calculate the total sum of all digits
  h='',  // Init string
  s = 0, // Init sum 
  i = w, // Init running counter for delimiters
  [for(c of g)(
    s -= -c, // compute sum using minus to avoid string concatenation
    h += --i ? c : (i=w, c+':') // add current char + delimiter when needed
  )];


  // Recursive search
  // Paramaters:
  // g : current grid array, during search used digits are replaced with group letters
  // p : current position
  // k : current group id (start at n, decreaseing)
  // j : current group size, start at l/n decreasing, at 0 goto next group id
  // t : current group sum value, start at s/n decreasing

  var R=(g,p,k,j,t)=> 
  {
    var v = 0, // init value to return is 0
        h = String.fromCharCode(97+k); // group letter from group

    t-=g[p]; // subtract current digit

    if (t<0) // exceed the sum value, return 0 to stop search and backtrak
      return 0;

    g=[...g]; // build a new array from orginal parameter
    g[p] = h; // mark current position

    // Utility function  to scan grid array
    // call worker function  f only for digit elements
    //   skipping group markers, row delimieters and out of grid values (that are undefined)  
    // Using .some will return ealry if f returns truthy  
    var S=f=>g.some((c,p)=>c<':'&&f(p));

    if (--j) // decrement current group size, if 0 then group completed
    { // if not 0
      // Scan grid to find cells adiacent to current group and call R for each 
      S( p => {
        if (g[p+1]==h|g[p-1]==h|g[p+w+1]==h|g[p-w-1]==h) // check if adiacent to a mark valued h
        {
          return v=R(g,p,k,j,t) // set v value and returns it
        }
      })
      // here v could be 0 or a full grid 
    }
    else
    {
      // special case: at first call, t is be NaN because p -1 (outside the grid)
      // to start a full grid serach
      if (t) // check if current reached 0
        return 0; // if not, return 0 to stop search and backtrak


      if (k) // check if current group completed
      {
        // if not at last group, recursive call to R to check next group 
        S( p => {
          // exec the call for each valid cell still in grid
          // params j and t start again at init values
          return v=R(g,p,k-1,l/n,s/n) // set value v and returns it
        })
        // here v could be 0 or a full grid 
      }
      else
      {
        return g; // all groups checked, search OK, return grid with all groups marked
      }
    }
    return v
  };
  g = h; // set g = h, so g has the row boundaries and all the digits

  h=R(h,-1,n,1); // first call with position -1 to and group size 1 to start a full grid search

  if (h) // h is the grid with group marked if search ok, else h is 0
  {
    o = ''; // init output string
    // build output string merging the group marks in h and the original digits in g
    h.map( (c,p) => o += c>':' ? g[p]+c: '') // cut delimiter ':'
    return o;
  }
  return 'None';
}

Test In FireFox/FireBug console

F("156790809",3) output 1c5c6b7a9c0b8a0a9b

F("156790819",3) output None

\$\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.