14.2 Composite Operators and Check Solution

14.2.1 Composite Operators

Now, we are ready to automate the three-step composite operators process. Let’s say we consider the number sequence in the second row of nums_perm: 5, 1, 5, 5, with the ninth row of op_mat: 1, 2, 3. We know the composite operation goes as below along with the corresponding steps printed.

re_1 <- cal(nums_perm[2, 1], nums_perm[2, 2], op_mat[9, 1])
print_op(nums_perm[2, 1], nums_perm[2, 2], re_1, op_mat[9, 1])
#> [1] "5 + 1 = 6"
re_2 <- cal(re_1, nums_perm[2, 3], op_mat[9, 2])
print_op(re_1, nums_perm[2, 3], re_2, op_mat[9, 2])
#> [1] "6 - 5 = 1"
re_3 <- cal(re_2, nums_perm[2, 4], op_mat[9, 3])
print_op(re_2, nums_perm[2, 4], re_3, op_mat[9, 3])
#> [1] "1 * 5 = 5"

To make our code more compact, we can write a loop over the three operators.

sol <- rep(0, n_nums)
sol[1] <- nums_perm[2, 1]
for (step in 1:(n_nums - 1)) {
    sol[step + 1] <- cal(sol[step], nums_perm[2, step + 1], op_mat[9, step])
    cat(print_op(sol[step], nums_perm[2, step + 1], sol[step + 1], op_mat[9, step]),
        "\n")
}
#> 5 + 1 = 6 
#> 6 - 5 = 1 
#> 1 * 5 = 5

To prepare this step as a standalone function for computing all the steps, let’s write a function.

num_op <- function(num, op, n_nums = length(num)) {
    sol <- num
    for (step in 1:(n_nums - 1)) sol[step + 1] <- cal(sol[step], num[step + 1], op[step])
    return(sol)
}
sol <- num_op(nums_perm[2, ], op_mat[9, ])
sol
#> [1] 5 6 1 5

14.2.2 Check if Solution is Found

Once we finish the composite operations, we can check whether our final result is equal to the target value, 24.

goal <- 24
if (abs(sol[n_nums] - goal) < 1e-05) {
    cat("Found a solution!")
} else {
    cat("This is not a solution.")
}
#> This is not a solution.

If it does, we can print the solution. Let’s write another function to print all the intermediate steps.

print_sol <- function(nums, sol, op) {
    sol <- round(sol, 3)  #Only keep 3 digits to make it better looking.
    n_nums <- length(sol)
    for (step in 1:(n_nums - 1)) {
        cat(print_op(sol[step], nums[step + 1], sol[step + 1], op[step]), "\n")
    }
}
print_sol(nums_perm[2, ], sol, op_mat[9, ])
#> 5 + 1 = 6 
#> 6 - 5 = 1 
#> 1 * 5 = 5

14.2.3 Looping Over All Number Sequences and All Operator Sequences

Now, we know how to compute the final value given a number sequence and a operator sequence. Let’s write double loop over the number sequences and the operator sequences. Note that we only want to print the steps when we have found a solution.

n_sols <- 0
for (i in 1:nrow(nums_perm)) for (j in 1:nrow(op_mat)) {
    num_cur <- nums_perm[i, ]
    op_cur <- op_mat[j, ]
    sol <- num_op(num_cur, op_cur)
    if (abs(sol[n_nums] - goal) < 1e-05) {
        n_sols <- n_sols + 1
        cat("Solution #", n_sols, "\n")
        print_sol(num_cur, sol, op_cur)
    }
}
#> Solution # 1 
#> 1 / 5 = 0.2 
#> 5 - 0.2 = 4.8 
#> 4.8 * 5 = 24
if (n_sols == 0) {
    cat("Solution not exist!")
}

From the result, we can see that there is one solution to this difficult problem.