KenKen puzzle addends: REDUX A (corrected) non-recursive algorithm

后端 未结 9 1663
眼角桃花
眼角桃花 2021-01-03 05:24

This question relates to those parts of the KenKen Latin Square puzzles which ask you to find all possible combinations of ncells numbers with values x such that 1 <= x &

相关标签:
9条回答
  • 2021-01-03 05:59

    First of all, I am learning Python myself so this solution won't be great but this is just an attempt at solving this. I have tried to solve it recursively and I think a recursive solution would be ideal for this kind of problem although THAT recursive solution might not be this one:

    def GetFactors(maxVal, noOfCells, targetSum):
        l = []
        while(maxVal != 0):
            remCells = noOfCells - 1
            if(remCells > 2):
                retList = GetFactors(maxVal, remCells, targetSum - maxVal)
                #Append the returned List to the original List
                #But first, add the maxVal to the start of every elem of returned list.
                for i in retList:
                    i.insert(0, maxVal)
                l.extend(retList)
    
            else:
                remTotal = targetSum - maxVal
                for i in range(1, remTotal/2 + 1):
                    itemToInsert = remTotal - i;
                    if (i > maxVal or itemToInsert > maxVal):
                        continue
                    l.append([maxVal, i, remTotal - i])
            maxVal -= 1
        return l
    
    
    
    if __name__ == "__main__":
        l = GetFactors(5, 5, 15)
        print l
    
    0 讨论(0)
  • 2021-01-03 06:08

    Here's the simplest recursive solution that I can think of to "find all possible combinations of n numbers with values x such that 1 <= x <= max_val and x(1) + ... + x(n) = target". I'm developing it from scratch. Here's a version without any optimization at all, just for simplicity:

    def apcnx(n, max_val, target, xsofar=(), sumsofar=0):
      if n==0:
        if sumsofar==target:
          yield xsofar
        return
    
      if xsofar:
        minx = xsofar[-1] - 1
      else:
        minx = 0
    
      for x in xrange(minx, max_val):
        for xposs in apcnx(n-1, max_val, target, xsofar + (x+1,), sumsofar+x+1):
          yield xposs
    
    for xs in apcnx(4, 6, 12):
      print xs
    

    The base case n==0 (where we can't yield any more numbers) either yield the tuple so far if it satisfies the condition, or nothing, then finishes (returns).

    If we're supposed to yield longer tuples than we've built so far, the if/else makes sure we only yield non-decreasing tuples, to avoid repetition (you did say "combination" rather than "permutation").

    The for tries all possibilities for "this" item and loops over whatever the next-lower-down level of recursion is still able to yield.

    The output I see is:

    (1, 1, 4, 6)
    (1, 1, 5, 5)
    (1, 2, 3, 6)
    (1, 2, 4, 5)
    (1, 3, 3, 5)
    (1, 3, 4, 4)
    (2, 2, 2, 6)
    (2, 2, 3, 5)
    (2, 2, 4, 4)
    (2, 3, 3, 4)
    (3, 3, 3, 3)
    

    which seems correct.

    There are a bazillion possible optimizations, but, remember:

    First make it work, then make it fast

    I corresponded with Kent Beck to properly attribute this quote in "Python in a Nutshell", and he tells me he got it from his dad, whose job was actually unrelated to programming;-).

    In this case, it seems to me that the key issue is understanding what's going on, and any optimization might interfere, so I'm going all out for "simple and understandable"; we can, if need be!, optimize the socks off it once the OP confirms they can understand what's going on in this sheer, unoptimized version!

    0 讨论(0)
  • 2021-01-03 06:12

    Sorry to say, your code is kind of long and not particularly readable. If you can try to summarize it somehow, maybe someone can help you write it more clearly.

    As for the problem itself, my first thought would be to use recursion. (For all I know, you're already doing that. Sorry again for my inability to read your code.) Think of a way that you can reduce the problem to a smaller easier version of the same problem, repeatedly, until you have a trivial case with a very simple answer.

    To be a bit more concrete, you have these three parameters, max_val, target_sum, and n_cells. Can you set one of those numbers to some particular value, in order to give you an extremely simple problem requiring no thought at all? Once you have that, can you reduce the slightly harder version of the problem to the already solved one?

    EDIT: Here is my code. I don't like the way it does de-duplication. I'm sure there's a more Pythonic way. Also, it disallows using the same number twice in one combination. To undo this behavior, just take out the line if n not in numlist:. I'm not sure if this is completely correct, but it seems to work and is (IMHO) more readable. You could easily add memoization and that would probably speed it up quite a bit.

    def get_combos(max_val, target, n_cells):
        if target <= 0:
            return []
        if n_cells is 1:
            if target > max_val:
                return []
            else:
                return [[target]]
        else:
            combos = []
            for n in range(1, max_val+1, 1):
                for numlist in get_combos(max_val, target-n, n_cells-1):
                    if n not in numlist:
                        combos.append(numlist + [n])
            return combos
    
    def deduplicate(combos):
        for numlist in combos:
            numlist.sort()
        answer = [tuple(numlist) for numlist in combos]
        return set(answer)
    
    def kenken(max_val, target, n_cells):
        return deduplicate(get_combos(max_val, target, n_cells))
    
    0 讨论(0)
提交回复
热议问题