Using Python to Teach About Finding the Digital Root

My wife teaches 3rd grade math and she recently learned about the process of obtaining the digital root of numbers. The digital root is a single digit number found by summing the individual digits. So for 15, you would add 1+5 to get 6. Thus 6 is the digital root of 15. The trainer that my wife worked with explained that you can check your answers using the digital root, which will give children another way to find out if their answer is correct. Here is an example:

 15   1 + 5 = 6
+12   1 + 2 = 3
      6 + 3 = 9
----
 27   2 + 7 = 9

So here we have two operands: 15 and 12. If you add those together, you get 27. To check your answer using the digital root, you add the individual digits in the two operands as above. So 15 becomes 1+5 or 6 and 12 becomes 1+2 or 3. Then you add those two roots together to get 9. Then you check your answer by adding up its digits, which in this case is 2+7 which equals 9. The rules are slightly different for subtraction, multiplication and division. We’ll be looking at addition, subtraction and multiplication. We are skipping division because I haven’t found a good explanation for how it works and I don’t want to just use formulas that I can’t explain.

At this point you’re probably wonder where Python comes in. We’ll use wxPython to create a simple GUI that will allow us to see how this works. Let’s start coding!

Creating a GUI for Finding Digital Roots

digiroot

Creating the GUI was a little tricky at first, but quickly becomes easier as you learn how digital roots work. The only package you will need to follow along is wxPython, which you can get here. Once you have that installed, you’ll be good to go. Now let’s take a look at the code:

import wx

########################################################################
class DigiPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        
        size = (50, -1)
        firstNumLbl = wx.StaticText(self, size=size)
        self.firstNum = wx.TextCtrl(self, size=size)
        self.digitalRootOne = wx.StaticText(self)
        
        operators = ["+", "-", "x"]
        self.operatorsCbo = wx.ComboBox(self, value="+", choices=operators,
                                        size=size)
        self.secondNum = wx.TextCtrl(self, size=size)
        self.digitalRootTwo = wx.StaticText(self)
        
        self.digiRootLbl = wx.StaticText(self, size=(108, -1))
        self.digitalRootAnswer = wx.StaticText(self)
        
        totalLbl = wx.StaticText(self, size=size, label="Answer:")
        self.total = wx.TextCtrl(self, size=size, style=wx.TE_READONLY)
        self.totalRoot = wx.StaticText(self)
        
        calcBtn = wx.Button(self, label="Calculate")
        calcBtn.Bind(wx.EVT_BUTTON, self.onCalculate)
        digiRootBtn = wx.Button(self, label="Find Digital Root")
        digiRootBtn.Bind(wx.EVT_BUTTON, self.onDigitalRoot)
        
        # layout the widgets
        mainSizer= wx.BoxSizer(wx.VERTICAL)
        firstSizer = wx.BoxSizer(wx.HORIZONTAL)
        secondSizer = wx.BoxSizer(wx.HORIZONTAL)
        totalSizer = wx.BoxSizer(wx.HORIZONTAL)
        digiAnswerSizer = wx.BoxSizer(wx.HORIZONTAL)
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
        
        firstSizer.Add(firstNumLbl, 0, wx.ALL, 5)
        firstSizer.Add(self.firstNum, 0, wx.ALL, 5)
        firstSizer.Add(self.digitalRootOne, 0, wx.ALL, 5)
        mainSizer.Add(firstSizer, 0, wx.ALL, 5)
        
        secondSizer.Add(self.operatorsCbo, 0, wx.ALL, 5)
        secondSizer.Add(self.secondNum, 0, wx.ALL, 5)
        secondSizer.Add(self.digitalRootTwo, 0, wx.ALL, 5)
        mainSizer.Add(secondSizer, 0, wx.ALL, 5)
        
        digiAnswerSizer.Add(self.digiRootLbl, 0, wx.ALL, 5)
        digiAnswerSizer.Add(self.digitalRootAnswer, 0, wx.ALL, 5)
        mainSizer.Add(digiAnswerSizer, 0, wx.ALL, 5)
        
        totalSizer.Add(totalLbl, 0 ,wx.ALL, 5)
        totalSizer.Add(self.total, 0, wx.ALL, 5)
        totalSizer.Add(self.totalRoot, 0, wx.ALL, 5)
        mainSizer.Add(totalSizer, 0, wx.ALL, 5)
        
        btnSizer.Add(calcBtn, 0, wx.ALL|wx.CENTER, 5)
        btnSizer.Add(digiRootBtn, 0, wx.ALL|wx.CENTER, 5)
        mainSizer.Add(btnSizer, 0, wx.ALL|wx.CENTER, 5)
        
        self.SetSizer(mainSizer)
        
    #----------------------------------------------------------------------
    def getAddString(self, number):
        """
        Calculate the digital string
        """
        if "-" in str(number):
            answer = number
            strNumbers = [str(number)]
        else:
            strNumbers = [i for i in str(number)]
            answer = sum(map(int, strNumbers))
        newAnswer = None
        root = " + ".join(strNumbers) + " = " + str(answer)
        
        while answer >= 10:
            strNumbers = [i for i in str(answer)]
            answer = sum(map(int, strNumbers))
            newRoot = " + ".join(strNumbers) + " = " + str(answer)
            root += " => " + newRoot
        
        return root, answer
        
    #----------------------------------------------------------------------
    def getMultiString(self, number, number2):
        """
        Get the string associated with multiplication
        """
        answer = number * number2
        root = "%s x %s = %s" % (number, number2, answer)
        newAnswer = 1
        
        if answer >= 10:
            root += " => " + self.getAddString(answer)[0]
        return root
    
    #----------------------------------------------------------------------
    def getSubtractionString(self, number, number2, total=None):
        """
        Get subtraction string
        """
        if not total:
            answer = number - number2
            root = "%s - %s = %s" % (number, number2, answer)
        else:
            root = "%s" % total
            answer = total
            
        if answer < 0:
            # then we need to add 9
            newAns = answer + 9
            root += " => %s + 9 = %s" % (answer, newAns)
        return root
        
    #----------------------------------------------------------------------
    def onCalculate(self, event):
        """
        Calculate the total
        """
        firstNum = int( self.firstNum.GetValue() )
        secondNum = int( self.secondNum.GetValue() )
        operator = self.operatorsCbo.GetValue()
        
        if operator == "+":
            total = firstNum + secondNum
        elif operator == "x":
            total = firstNum * secondNum
        elif operator == "-":
            total = firstNum - secondNum
            
        self.total.SetValue(str(total))
        
    #----------------------------------------------------------------------
    def onDigitalRoot(self, event):
        """
        Show digital root
        """
        firstNum = int( self.firstNum.GetValue() )
        secondNum = int( self.secondNum.GetValue() )
        total = int(self.total.GetValue())
        operator = self.operatorsCbo.GetValue()
        
        firstRoot, firstAnswer = self.getAddString(firstNum)
        secondRoot, secondAnswer = self.getAddString(secondNum)
        
        if operator == "+":
            totalRoot, _ = self.getAddString(total)
            rootTotal = sum([firstAnswer, secondAnswer])
            ans = "%s + %s = %s" % (firstAnswer, secondAnswer, rootTotal)
            if rootTotal >= 10:
                root, _ = self.getAddString(rootTotal)
                ansRoot += " => " + root
            else:
                ansRoot = ans
            
            self.digiRootLbl.SetLabel("A + B = root")
        elif operator == "x":
            ansRoot = self.getMultiString(firstAnswer, secondAnswer)
            totalRoot, _ = self.getAddString(total)
            self.digiRootLbl.SetLabel("A x B = root")
        elif operator == "-":
            ansRoot = self.getSubtractionString(firstAnswer, secondAnswer)
            totalRoot = self.getSubtractionString("", "", total)
            self.digiRootLbl.SetLabel("A - B = root")
            
            
        self.digitalRootOne.SetLabel("A: " + firstRoot)
        self.digitalRootTwo.SetLabel("B: " + secondRoot)
        self.digitalRootAnswer.SetLabel(ansRoot)
        self.totalRoot.SetLabel(totalRoot)
            
    
########################################################################
class DigiFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        title = "Digital Root Finder"
        size = (800, 600)
        wx.Frame.__init__(self, None, title=title, size=size)
        panel = DigiPanel(self)
        self.Show()
        
#----------------------------------------------------------------------
def main():
    """
    Run the program
    """
    app = wx.App(False)
    frame = DigiFrame()
    app.MainLoop()
        
if __name__ == "__main__":
    main()

Here we create a bunch of widgets in rows. You could use a wx.GridBagSizer or similar rather than nesting BoxSizers, but I prefer the flexibility of using BoxSizers even if it does get a bit more complicated. Anyway, we layout four rows of widgets plus a smaller row of buttons. Some of the static text widgets act as spacers to help align items. We have two buttons; one for calculating the answer and one for finding the digital root. The calculate button checks the combo box to find out which operation it should do and then acts accordingly. The other button’s event handler, onDigitalRoot, is quite a bit more complex.

Once again we check which operator has been chosen (+, – or x) and then we figure out the digital roots. For the addition, we actually end up calling getAddString where we check if the number is negative or not. If it is, we don’t add the digits. Yes, this could be a problem if we ever get a value greater than -9, but if that happens, then we’ve done something else wrong. Anyway, if it’s above 9, then we need to sum up the widgets, which is what the following lines do for us:

strNumbers = [i for i in str(number)]
answer = sum(map(int, strNumbers))

We then use a while loop to keep adding up the answer’s digits in just in case we ever get a really large operand. Since this is mostly for grade schoolers, this is overkill, but I know someone would have asked why we weren’t checking. The subtraction method is pretty similar. The biggest difference is that if the digital root answer is less than zero, you have to add nine. This is the complement (i.e. the amount needed to make it whole). I haven’t really found a more in depth answer as to why you do that. Regardless, to get the subtraction digital root, you take the roots of the operands and subtract them from each other.

Here’s an example: Say you have 15 – 12 = 3. The 15 becomes 1+5 or 6 and the 12 becomes 1+2 or 3. So now you take 6-3 = 3 which is the same as 15-12.

The multiplication is pretty easy too. Let’s do a quick example: 12 x 10 = 120. The 12 becomes 1+2 or 3 and the 10 becomes 1+0 or 1. Since this is multiplication, you then take 3×1 to get 3. The answer is 120, but its digital root is 1+2+0 or 3.

Wrapping Up

Now you know how to find the digital root in 3 out of 4 type of arithmetic. And you learned how to create a simple GUI that can display how it’s done as well. Math and Python rock!

Additional Reading

Download the Source