I have an interesting real world problem. My brother is currently in school. The teachers perform the following procedure when giving him his grades. They take his list of grades grades (which are non-negative real numbers that are multiples of 0.1 - ex. 95.3, 86, 103.4), duplicate some or none of them to form the list duplicated_grades, calculate the average of duplicated_grades rounded to the tenths place dup_avg = round_tenth(average(duplicated_grades)), and then give him the list grades and the average dup_avg. My mother wants to know which of the grades in grades could have been duplicated to form dup_avg.
To provide some examples:
input:
grades = ["100", "90", "80"]
dup_avg = "90"
desired output:
no duplicates
1 90
1 100, 1 80
1 100, 1 90, 1 80
no duplicates means that duplicating none of the grades in grades and then taking the average results in dup_avg
1 90 means that duplicating one 90 in grades and then taking the average results in dup_avg
1 100, 1 80 means that duplicating one 100 and one 80 in grades and then taking the average results in dup_avg
input:
grades = ["40", "70", "90", "70", "90"]
dup_avg = "67.1"
desired output:
1 70, 1 40
I developed a recursive solution that works well for this problem, shown below:
from treelib import Node, Tree
from decimal import localcontext, Decimal, ROUND_HALF_UP
class SNA(object):
def __init__(self, sum, num, avg):
self.sum = sum
self.num = num
self.avg = avg
def find_dup_grades(grades, dup_avg):
with localcontext() as ctx:
ctx.rounding = ROUND_HALF_UP
grades = [Decimal(g) for g in grades]
dup_avg = Decimal(dup_avg)
uniq_grades = set()
for g in grades:
uniq_grades.add(g)
desc_u_grad = list(uniq_grades)
desc_u_grad.sort(reverse=True)
grade_count = {}
for g in uniq_grades:
grade_count[g] = grades.count(g)
tree = Tree()
s = sum(grades)
n = len(grades)
a = round(s / n, 1)
tree.create_node("root", "root", data=SNA(s, n, a))
fdg_helper(tree, grade_count, desc_u_grad, dup_avg, 0, "root")
# print(tree.show(data_property="avg", stdout=False))
def fdg_helper(tree, grade_count, desc_u_grad, dup_avg, i, pname):
pdata = tree.get_node(pname).data
if i == len(desc_u_grad):
if pdata.avg == dup_avg:
print(solution(desc_u_grad, pname))
return
if pdata.avg < dup_avg and desc_u_grad[i] < dup_avg:
return
for j in range(grade_count[desc_u_grad[i]] + 1):
name = "{0}{1}".format(pname, j)
s = pdata.sum + (j * desc_u_grad[i])
n = pdata.num + j
a = round(s / n, 1)
tree.create_node(name, name, parent=pname, data=SNA(s, n, a))
fdg_helper(tree, grade_count, desc_u_grad, dup_avg, i+1, name)
def solution(desc_u_grad, pname):
sol = ""
for i, val in enumerate(pname[4:]):
if val != "0":
sol += "{0} {1}, ".format(val, desc_u_grad[i])
if sol == "":
return "no duplicates"
return sol[:-2]
Even though I have a solution that works, with a love for programming, I was curious if there are any other approaches to this problem. Maybe a solution with a faster runtime or that uses parallel programming. All approaches are welcome.
Also, please let me know if there is a better place to post a question of this nature. I will gladly move my question.