Beispiel #1
0
def simple_reduction(puzzle):
  """
  simple_reduction returns a solution to <puzzle>.

  It works by reducing the number of greenhouses one by one until it has the
  lowest cost and meets the max constraint.
  """
  max, field = puzzle

  # figure out the current number of greenhouses
  greenhouses = common.ids(field)

  # we need to keep a copy of the previous field and it's cost in order
  # to return it once we've realized we've done one reduction too many
  prev_field, prev_cost = None, sys.maxint
  if len(greenhouses) <= max:
    prev_field, prev_cost = copy.deepcopy(field), common.cost(field)

  # join greenhouses until when run out of them or until max constraint
  # is met *and* cost increases from one reduction to the next
  while len(greenhouses) > 1:
    j1, j2, js = 0, 0, sys.maxint
    # try each combination of greenhouses
    for g1, g2 in itertools.combinations(greenhouses, 2):
      # find outer bounds (left, right, top and bottom) for a greenhouse made
      # up of g1 and g2
      size3, p31, p32 = common.outer_bounds([g1, g2], field)

      if size3 is not None:
        size1, p11, p12 = common.outer_bounds(g1, field)
        size2, p21, p22 = common.outer_bounds(g2, field)

        diff = size3 - size2 - size1
        if diff < js:
          j1, j2, js = g1, g2, diff

    # if we run out of combinations to try
    # we must either surrender (return None)
    # or if len(greenhouses) <= max return
    # the best solution we have.
    if j1 == 0:
      if len(greenhouses) <= max:
        return max, prev_field
      else:
        return max, None

    # join j1 and j2, remove j2 from greenhouses
    field = common.join(j1, j2, field)
    greenhouses.remove(j2)

    # decide if we should exit this loop or keep on reducing
    curr_cost = common.cost(field)
    if len(greenhouses) < max:
      if prev_cost < curr_cost:
        return max, prev_field

    prev_field, prev_cost = copy.deepcopy(field), curr_cost

  # if we end up here, we've come down to 1 greenhouse
  return max, field
Beispiel #2
0
def search(puzzle, breakpoint = 2):
  """
  search produces a solution to <puzzle>.

  >>> solve("p3.text") # doctest: +ELLIPSIS
  71
  ...
  """
  max, field = puzzle

  solution = (common.cost(field), field)

  paths = [solution, ]
  while len(paths) > 0:
    curr_cost, field = paths.pop(0)

    # Figure out the current number of greenhouses
    greenhouses = common.ids(field)

    if len(greenhouses) > 1:
      diffs = {}
      # Try each combination of greenhouses
      for g1, g2 in itertools.combinations(greenhouses, 2):
        # Find outer bounds (left, right, top and bottom) for a greenhouse made
        # up of g1 and g2
        size3, p31, p32 = common.outer_bounds([g1, g2], field)

        if size3 is not None:
          size1, p11, p12 = common.outer_bounds(g1, field)
          size2, p21, p22 = common.outer_bounds(g2, field)

          diff = size3 - size2 - size1
          if diff not in diffs.keys():
            diffs[diff] = [(g1, g2),]
          else:
            diffs[diff].append((g1, g2))

      # Find the list of joins which has the lowest diff and select the joins
      # of the most frequent greenhouse.
      if len(diffs.keys()) > 0:
        freqs = {}
        for (g1, g2) in diffs[sorted(diffs.keys())[0]]:
          if g1 not in freqs.keys():
            freqs[g1] = [(g1, g2),]
          else:
            freqs[g1].append((g1, g2))

          if g2 not in freqs.keys():
            freqs[g2] = [(g1, g2),]
          else:
            freqs[g2].append((g1, g2))

        # Perform each join in a fresh copy of field and add it to paths if
        # cost is lower than current cost, otherwise compare cost to solution
        # and either discard this path or add it as best-so-far.
        joins = freqs[sorted(freqs.keys(), key = lambda k: len(freqs[k]), reverse = True)[0]]
        if len(joins) <= breakpoint:
          (g1, g2) = joins[0]
          _, _field = s1.simple_reduction((max, common.join(g1, g2, copy.deepcopy(field))))

          cf = common.cost(_field)
          if cf < solution[0] and \
             len(common.ids(_field)) <= max:

            solution = (cf, _field)

        else:
          for (g1, g2) in joins:
            _field = common.join(g1, g2, copy.deepcopy(field))
            cf = common.cost(_field)
            if cf < curr_cost:
              paths.append((cf, _field))

              if cf < solution[0] and \
                 len(common.ids(_field)) <= max:

                solution = (cf, _field)

  return max, solution[1]