Google Foobar : Doomsday Fuel Not passing hidden test cases

51 Views Asked by At

I have a google foobar question that involves absorbing markov chains and I've come up with code that I made using a theoretical answer I found on github and it seems to work pretty well but it doesnt pass Hidden Test Case #5 due to unknown reasons. Question:

Making fuel for the LAMBCHOP's reactor core is a tricky process because of the exotic matter involved. It starts as raw ore, then during processing, begins randomly changing between forms, eventually reaching a stable form. There may be multiple stable forms that a sample could ultimately reach, not all of which are useful as fuel.

Commander Lambda has tasked you to help the scientists increase fuel creation efficiency by predicting the end state of a given ore sample. You have carefully studied the different structures that the ore can take and which transitions it undergoes. It appears that, while random, the probability of each structure transforming is fixed. That is, each time the ore is in 1 state, it has the same probabilities of entering the next state (which might be the same state). You have recorded the observed transitions in a matrix. The others in the lab have hypothesized more exotic forms that the ore can become, but you haven't seen all of them.

Write a function solution(m) that takes an array of array of nonnegative ints representing how many times that state has gone to the next state and return an array of ints for each terminal state giving the exact probabilities of each terminal state, represented as the numerator for each state, then the denominator for all of them at the end and in simplest form. The matrix is at most 10 by 10. It is guaranteed that no matter which state the ore is in, there is a path from that state to a terminal state. That is, the processing will always eventually end in a stable state. The ore starts in state 0. The denominator will fit within a signed 32-bit integer during the calculation, as long as the fraction is simplified regularly.

from fractions import Fraction
def subtract(matrix1, matrix2):
    result = [[a - b for a, b in zip(row1, row2)] for row1, row2 in zip(matrix1, matrix2)]
    return result
    
def matrix_minor(matrix, row, col):
    return [[matrix[i][j] for j in range(len(matrix[i])) if j != col] for i in range(len(matrix)) if i != row]

def determinant(matrix):
    if len(matrix) == 1:
        return matrix[0][0]
    elif len(matrix) == 2:
        return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]
    else:
        det = 0
        for col in range(len(matrix[0])):
            det += ((-1) ** col) * matrix[0][col] * determinant(matrix_minor(matrix, 0, col))
        return det

def transpose(matrix):
    return [[matrix[j][i] for j in range(len(matrix))] for i in range(len(matrix[0]))]

def cofactor(matrix):
    cofactors = [[(((-1) ** (i + j)) * determinant(matrix_minor(matrix, i, j))) for j in range(len(matrix[i]))] for i in range(len(matrix))]
    return cofactors

def scalar_multiply(matrix, scalar):
    return [[element * scalar for element in row] for row in matrix]

def inverse(matrix):
    det = determinant(matrix)
    cofactors = cofactor(matrix)
    adjugate = transpose(cofactors)
    inverse = scalar_multiply(adjugate, 1 / det)
    return inverse
    
def multiply(matrix1, matrix2):
    result = [[0 for _ in range(len(matrix2[0]))] for _ in range(len(matrix1))]
    for i in range(len(matrix1)):
        for j in range(len(matrix2[0])):
            for k in range(len(matrix2)):
                result[i][j] += matrix1[i][k] * matrix2[k][j]
    return result
    
def solution(m):
    term = []
    nonterm = []
    if len(m) == 1:
        frac=Fraction(1-m[0][0]).limit_denominator()
        return [frac.numerator, frac.denominator]
            
    for i in range(len(m)):
        if sum(m[i]) == 0:
            term.append(i)
        else:
            nonterm.append(i)
            
    if 0 in term:                              
        return [1] + [0]*(len(term)-1) + [1]
        

    new_matrix = [m[i] for i in nonterm]
    row_sums = [sum(row) for row in m]
    non_zero_rows = [i for i in range(len(m)) if row_sums[i] != 0]
    P = [[Fraction(m[i][j], row_sums[i]) for j in range(len(m[i]))] for i in non_zero_rows]
    Q = [[row[i] for i in nonterm] for row in P]
    R = [[row[i] for i in term] for row in P]
    size = len(Q)
    I = [[1.0 if i == j else 0.0 for j in range(size)] for i in range(size)]
    intermediate=subtract(I,Q)
    N=inverse(intermediate)
    B=multiply(N,R)
    B=B[0]
    fractions_list = [Fraction(prob).limit_denominator() for prob in B]
    common_denominator = max(f.denominator for f in fractions_list)
    numerators = [f.numerator * (common_denominator // f.denominator) for f in fractions_list]
    return numerators + [common_denominator]

I've tried to handle as many edge cases as I can think of such as singular state , intial state being terminal , decimal inaccuracies( by using fractions from the very begining),ran it on python 2.7 which is the target env. I know this question does not have debugging details but that is because the test cases are hidden

Edit: The issue has been solved the issue was with how i processed my probability fractions

i replaced

fractions_list = [Fraction(prob).limit_denominator() for prob in B]
    common_denominator = max(f.denominator for f in fractions_list)
    numerators = [f.numerator * (common_denominator // f.denominator) for f in fractions_list]
return numerators + [common_denominator]

with a function that does almost the same

def outputFormat(probabilities):
    res = []
    denominator = probabilities[0]._denominator
    for probability in probabilities[1:]:
        denominator = lcm(denominator, probability._denominator)
    for probability in probabilities:
        res.append(
            probability._numerator * (denominator / probability._denominator))
    res.append(denominator)
    return res

0

There are 0 best solutions below