def all_NaN(matrix): for row in matrix: for x in row: if not isNaN(x): # NaN is not equal to itself, so we have an actual number here return False return True
def jap_score(m, sol): if all_NaN(m): raise ValueError("Matrix is all NaNs.") total = 0 for i in range(len(m)): for j in range(len(m[0])): mm = m[i][j] ss = sol[i][j] # watch out for NaNs if isNaN(mm): # skip this element, so we don't have NaN scores for subproblems continue total += mm * ss if total != total: # NaN raise ValueError( "Score is NaN for matrix {0} and solution {1}.".format(m, sol)) return total
def get_second_bests(normalized_matrix): """ Takes a normalized_matrix in which every row has a maximum value of zero. Returns a list of the maximum non-zero value in each row. """ if max([max(row) for row in normalized_matrix]) > 0: raise ValueError("Matrix must have maximum value of zero.") result = [] for row in normalized_matrix: non_zeros = [i for i in filter(lambda x: x != 0, row)] if non_zeros == []: # all values in the row are zero; i.e. the same result.append(0) else: non_NaNs = [i for i in filter(lambda x: not isNaN(x), non_zeros)] if non_NaNs == []: result.append(NaN) else: result.append(max(non_NaNs)) return result
def nchildren(self, v=0): v = int(v) assert not isNaN(v) # JS wont fail but make NaN return v
def age(self, v=0): v = float(v) assert not isNaN(v) # JS wont fail but make NaN return v
def yell(self, v): v = int(v) assert not isNaN(v) # JS wont fail but make NaN return {'action': 'yell', 'value': v}
def jap(m): """ Takes input m (the n*m matrix where element (i,j) tells how fit person i is for job j). Returns an n*m matrix where element (i,j) is 1 if person i is assigned to job j and 0 otherwise. """ original_m = [[m[i][j] for j in range(len(m[0]))] for i in range(len(m))] # print("original matrix:",original_m) # Note that we assume WLOG n <= m, so we assign each person to one job. # If there are more people than jobs, then note the symmetry of the problem, so just transpose m, solve, and then transpose the result. need_to_transpose = len(m) > len(m[0]) if need_to_transpose: m = transpose(m) N = len(m) M = len(m[0]) pairs = [] # to be list of tuples (set of tuples could also work) #if all_NaN(m): # if it's somehow already all NaN #raise ValueError("Input matrix is all NaNs.") limiter = 0 limit = 10**4 while not all_NaN(m): # m will be all NaNs when we are done assigning jobs limiter += 1 if limiter > limit: raise RuntimeError("While loop ran too many times.") # print("this remnant of original matrix:",m) for r in range(N): # for each row row_without_NaN = [i for i in filter(lambda x: not isNaN(x), m[r])] if row_without_NaN == []: # print("skipping normalizing row {0} due to all NaN".format(r)) continue best = max(row_without_NaN) # best fit value for this person # print("best val:",best) m[r] = [ i - best for i in m[r] ] # subtract best value from everything in the row; everything will end up non-positive # print("normalized row:",m[r]) # print("THIS SHOULD NOT BE SKIPPED IN ANY CALL") # method: find the rows with the greatest gap between the best and the second-best columns; these must be given highest priority # keep in mind that a row which is all NaN will have been skipped already, so if second_bests is all NaN then there is one zero in the row # if there is just one zero in one row, then that pair should be part of the solution! this is most often the form of the base case best_pair = None best_pair_found = False second_bests = [ i for i in filter(lambda x: True, get_second_bests(m)) ] # not isNaN(x) if second_bests == []: # print("breaking because second_bests is empty") break elif all_NaN([second_bests]): singleton_rows = [ i for i in filter( lambda row: isNaN(second_bests[row]) and not all_NaN( [m[row]]), range(N)) ] # print("row indices with one non-NaN value:",singleton_rows) # print("breaking because second_bests is all NaN") best_pair = (singleton_rows[0], m[singleton_rows[0]].index(0) ) # just take the first one for now best_pair_found = True if not best_pair_found: lowest_second_best = min( [i for i in filter(lambda x: not isNaN(x), second_bests)]) # print("lowest_second_best:",lowest_second_best) #if isNaN(lowest_second_best): # this was causing problems because we needed to remove NaN before calling min() #break important_row_indices = [ i for i in filter( lambda r: second_bests[r] == lowest_second_best, range(N)) ] # prioritize the rows with the worst second-bests # print("next priority row indices (lowest_second_best = {0}): {1}".format(lowest_second_best,important_row_indices)) if len(important_row_indices) == 0: raise IndexError( "No rows selected for priority with matrix {0}.".format(m)) best_score = -Inf # best_pair = None # already initialized for important_row_index in important_row_indices: important_row = m[important_row_index] # print("this important row:",important_row) for zero_index in filter(lambda x: important_row[x] == 0, range(M)): # print("this zero index for row {0}: {1}".format(important_row,zero_index)) this_eliminated_matrix = eliminate(m, important_row_index, zero_index) # print("eliminating row and col to produce matrix",this_eliminated_matrix) if all_NaN(this_eliminated_matrix): best_pair = (important_row_index, zero_index) # print("breaking because elimination produced all NaNs, adding this pair to solution") break # print("making recursive call\n") subsolution = jap( this_eliminated_matrix ) # recursive call; watch out for treachery # print("next subsolution:",subsolution) # print("scoring matrix",this_eliminated_matrix) score = jap_score(this_eliminated_matrix, subsolution) if score > best_score: # print("subsolution accepted") best_score = score best_pair = (important_row_index, zero_index) else: pass # print("subsolution rejected") # print("best score found for subproblems:",best_score) pairs.append(best_pair) m = eliminate(m, best_pair[0], best_pair[1]) result = [[1 if (i, j) in pairs else 0 for j in range(M)] for i in range(N)] # turn the ordered pairs into sparse matrix # take out rows and columns by replacing them with NaN, then repeating procedure until whole matrix is NaN # DON'T ever actually remove rows or columns! This will make indices much harder to work with. # transpose back if we transposed before solving if need_to_transpose: result = transpose(result) # if not is_fully_assigned(result): # note that subproblem results will not be all assigned, so don't actually check for this # pass # raise ValueError("Result {0} has left some possible pairs unassigned.".format(result)) # print("returning result",result,"\n") return result