Tom Harris

If you can’t explain it simply

Written by Tom Harris

March 7, 2015 at 10:50 pm

Posted in Teaching

Insights from a Broken Calculator

Declaring the end from the beginning, and from ancient times things that are not yet done; saying: ‘My counsel shall stand, and all My pleasure will I do’

(Isaiah 46:10)

In elementary school we learned about numbers as if they were only for counting and calculating, skipping right over all the beauty of numbers and their meaning. So I was happy to have this “Calculator Puzzle” brought to my attention via a Twitter post. Not just for the mental exercise, but for the insights it brought about different parts of math (calculation, algebra, and geometry) to name a few, and about how my mind progresses from problem to solution. Definitely not by a straight line. But also not by a random walk.

If you want to try to solve the problems yourself, do NOT read this post, nor my replies on the Twitter thread!

That said, here was my thought process:

Background

calculator is not working properly. Only the add and reciprocal buttons work, as well as the numbers 0-9.

Well that was interesting. Reciprocal is not a key that I often use on a calculator.

Question 1

Can you work out how to halve any number?

I admit, my first reaction, and the one I went with, was to try a specific example first. And, as above in Isaiah, to “declare the end from the beginning”.

Say, half of 5. That would be 5/2.

How could I get from “5” and “half” to 5/2, with only “+” and “1/x”?

Impressed by the unusual button — reciprocal — I immediately asked: could I get 2/5?

Well, yes. 1/5 + 1/5 is 2/5.

That was it: 5, 1/x, +, 5, 1/x, = would give 2/5. Another press on 1/x would give 5/2. Q.E.D.

Insight 1a*

Better than solving the problem, though, is that as an electrical engineer, I felt a sense of “coming home”. I hadn’t seen it at first, but this was “reciprocal of the sum of the reciprocals”. Well known as the resistance of a set of resistors in parallel. So it would certainly work for every case, because two equal resistors is two equal paths instead of one, offering half the resistance. Whether you feel that math or electricity is the original depends on whether you’re a mathematician or an engineer.

* Where’s 1b?? You’ll have to wait for that. I did.

Question 2

Now the subtract button has started working.

I think this means I can now square any number – can you work out how?

Here, I rushed ahead too quickly to generalize. I offered a solution where squaring is a special case of multiplication. The original questioner gently encouraged me to try again without repeated addition.

Back to my specific example. 5. 5 squared is 25. No way I would get directly to 25, but now that I was friends with that reciprocal button, I sought 1/25.

After a few minutes of that, I realized that I would have to use the “-” button. Surely it was working (in the problem) for a reason.

1/25 is pretty small. I would get it by subtracting fractions (which I could generate with the 1/x button). But I couldn’t think of any fractions to subtract, certainly not with a numerator of 1, that would yield 1/25. I did realize that fractions with denominators close to each other would yield significantly smaller fractions. How about 1/4 – 1/5 = 1/20. OK, 1/20 was not 1/25, but maybe I could fix it up. 1/20 would yield 20 (after pressing 1/x). Then simple enough to add 5 and get to 25.

5 squared = 5 + 1 / (1/4 – 1/5).

I generalized: n^2 = n + 1 / (1/(n-1) – 1/n)

Why would it work in general? Algebra:

n + 1 / ( (n – (n-1)) / (n(n-1))) = n + 1 / 1 / (n(n-1)) = n + n(n-1) = n + n^2 – n = n^2.

Q.E.D. again.

Insight 2

Why is 1/4 – 1/5 = 1/20? What’s special there? Suddenly, though I had never seen it written down, I thought: “Consecutive integers are relatively prime.” Then I looked that up to be sure. I read some proof discussions, and then left off that to continue the problem.

Question 3

Now, I think I can also calculate the product of any number. How?

Here, I was onto the power of algebra to solve problems “automatically”. Instead of one number n, I would have two numbers x and y. I asked myself what product expressions involving x and y I was familiar with. The product (x+y)(x-y) came to mind. In high school it always seemed nice because it produced a sum of squares “cleanly” with no annoying middle term in xy. But here, I realized, “xy” was just what I was looking for. So the product would be the “messier” one: (x+y)(x+y). Which, by the way, is (x+y)^2. Aha! Is this is why the problem author tried to take me by the hand from square to product?

Anyway, (x+y)(x+y) = x^2 + 2xy + y^2. That could be solved for xy:

xy = ((x+y)(x+y)-(x^2 + y^2))/2.

Insight 3

I like geometrical interpretations more than, or at least in addition to, purely algebraic interpretations. Even if algebra works, I feel better seeing a picture too. So I went back and asked myself what picture matched that equation. Here it is (freehand drawing):

Insight 1b

Now, even though I had solved question 1 without any algebraic symbols, I challenged myself to find a geometric interpretation for the reciprocal of the sum of the reciprocals, with two equal numbers yielding a half.

One geometric interpretation of reciprocal is the (negative of the) slope of the perpendicular. With that in mind, the picture came pretty quickly:

Question 4

Are there any other operations I can do with only add, subtract and reciprocal?

I left this for when I find more time. Or maybe a reader would like to offer some thoughts? Not just the “answers”, but your thought process, and any insights that come up for you!

Written by Tom Harris

March 5, 2017 at 5:16 pm

Posted in Learning, Math

Writing, Unschooled

prompt /präm(p)t/ verb
2. assist or encourage (a hesitating speaker) to say something.

For example, “Write a 12 word story that uses the words ‘apple’ and ‘alarm’.”

My response:

“Leap, please,” cried the young orangutan, holding onto his mother’s feral arm.

My Writing CV

Experience

Responding to Tablo Publishing writing prompts, also on Twitter
Trying my hand at 50-word stories to accompany pictures on Seempli
Writing poetry on WhatsApp, inspired by friends
Years of e-mails, halved before sending, because people said, “Too Long!”
Studying French and Russian in college, to avoid having to write in English
Having my English writing labeled “tortured Teutonic” by my high school English teacher

Education

My engineering senior project professor who said, “You have a word processor. Just type everything you know, and rearrange it afterwards.”

Written by Tom Harris

October 11, 2016 at 11:57 am

Posted in Education, Writing

Learning a New Programming Language, with Life (part 4)

While preparing a course syllabus on coding in Python, for an upcoming high school class, I remembered static analysis. I haven’t decided how soon to introduce the topic, but I thought I’d better check how my own sample program fared.

No warnings or errors from Python itself. (If there had been I would have addressed them already!).

So I installed Pylint and tried its default settings. How about that. Not zero at all!

In fact, 54 coding convention messages, 12 warnings, and 1 recommendation for refactoring.

It was easy enough to clean up whitespace issues (helped to turn on “view whitespace” in my Notepad2).

And yes, many comment lines were too long. I left-justified (but indented for Python) for easy reading.

Some of the warnings are for my “TODO” comments — an extra reminder to do, or drop, next steps I’d identified earlier. Pylint message count is now down to 2 informational messages, 28 coding convention, 13 warnings, and 1 to refactor. They are valuable for my future Python learning (and teaching):

Locally disabling unused-variable (W0612) (locally-disabled)

I disabled those warnings because I know the code needs those variables. But I’ll have to explain why.

(Pylint doesn’t forget — the 2 new informational messages remind that I’ve suppressed two warnings.)

Invalid attribute name “maxRowMain” (invalid-name)

Not just that name. Most of my object names. I’ll have to find a good object-naming convention and use it.

R: 69, 4: Too many branches (13/12) (too-many-branches)

Just today I heard a Python lecturer on YouTube say, “if you’re not refactoring, you’re not learning.” Yes, that function is the longest. Not so complex, but could be simpler and easier to understand.

Attribute ‘maxRowMain’ defined outside __init__ (attribute-defined-outside-init)

A few of those also. I will have to go back and learn again about __init__: when to use it and why.

All in all, a good learning session, and direction on what I need to learn next.

Thanks to static analysis with Pylint.

Written by Tom Harris

September 2, 2015 at 10:30 pm

Learning a New Programming Language, with Life (part 3)

with one comment

I started learning Python three days ago, as described here in part 1 and part 2.

Just two hours a day, and I’m well on my way, because here below is a working implementation of my favorite, Conway’s Game of Life, in Python.

Today I refactored to put instance-specific initializations right in the functions that needed them, and promoted parsing of command line arguments to the main program. (Future: use argparse.)

I also realized that these functions are just procedures, so I stopped trying to return meaningful values. I removed the return statements. Python supports that, and returns None.

Update:

A day later, after questions from a reader led me to review my own code, I wondered why it works even without wraparound for negative indices!

The answer: Python lists handle negative indexes gracefully.

Let’s go to the code
``` #Life.py```

#Implementation of Conway’s Game of Life
#See https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life

#Tom Harris 22-Jul-2015
#Tom Harris 23-Jul-2015 add multiple generations and display period to main program

#Import libraries needed
import sys #for command line argument support

#Define the Life class

class Life:
….#class attributes
….#None

….#class methods
….#NOTE: Currently all procedures — no return statement, implicitly returns None
….
……..#Initialize (clear) the board of Life cells
……..self.mainBoard = []

……..#Open the file
……..initialGenerationFile = open(filename)

……..#Read from the file, line by line:
……..for line in initialGenerationFile:
…………listLine = list(line.rstrip(‘\r\n’))
…………self.mainBoard.append(listLine) #add the line, in Python list format, to the mainBoard

……..#Determine dimensions of the board after reading in
……..#WARNING: dimensions are 1 larger than largest index, since indices start at 0
……..self.maxRowMain = len(self.mainBoard)
……..self.maxColMain = len(self.mainBoard[0])

……..#Close the file
……..initialGenerationFile.close

….def displayBoard(self):
……..for rowIndex, row in enumerate(self.mainBoard):

…………for colIndex, col in enumerate(row):
…………….if self.mainBoard[rowIndex][colIndex]== “1”:
………………..print(“X”, end=”)
…………….else:
………………..print(“.”, end=”)

…………print() #print newline at end of each row

….def makeBoardCurrent(self): #TODO: Think of a better name — means to copy from the next-generation workingBoard to mainBoard
……..self.mainBoard = self.workingBoard
……..
….def calculateNextGeneration(self):
……..#Calculate next generation on workingBoard based on mainBoard

……..#Initialize work area
……..self.workingBoard = [] #A work area for creating the next Life generation

……..#Calculate next generation on workingBoard based on mainBoard
……..for mainRowIndex, mainRow in enumerate(self.mainBoard):

…………#Initialize working row before using for each row
…………workingRow = [] #for building up workingBoard row by row

…………for mainColIndex, mainElement in enumerate(mainRow):

…………….#Restart count of live cells
…………….numLiveCells = 0

…………….for i in [-1, 0, 1]:
………………..#Check row before, same row, next row
………………..rowOfCellToCheck = mainRowIndex + i

………………..#Wraparound
………………..if rowOfCellToCheck (self.maxRowMain – 1):
……………………rowOfCellToCheck = 0
……………………
………………..for j in [-1, 0, 1]:
……………………#Check column before, same column, next column
……………………colOfCellToCheck = mainColIndex + j

……………………#Wraparound
……………………if colOfCellToCheck (self.maxColMain – 1):
……………………….colOfCellToCheck = 0

……………………#If cell to check is alive, increment count of live cells
……………………#But if same row and column, ignore
……………………sameColAndRow = abs(i) + abs(j)
……………………if sameColAndRow != 0:
……………………….if self.mainBoard[rowOfCellToCheck][colOfCellToCheck] == “1”:
………………………. numLiveCells = numLiveCells + 1
…………………………………………

…………….if self.mainBoard[mainRowIndex][mainColIndex] == “1”: # The survival rules

………………..if 2 <= numLiveCells <= 3:
……………………workingRow.append(‘1’)
………………..else:
……………………workingRow.append(‘0’)

…………….else: #The reproduction rule

………………..if numLiveCells == 3:
……………………workingRow.append(‘1’)
………………..else:
……………………workingRow.append(‘0’)
……………………
…………self.workingBoard.append(workingRow)
…………………………..
#The actual main Life program
#Usage will be Life(filename, generations to calculate, every how many generations to display, display yes/no, write to files yes/no)
#First Usage Life() — done 22-Jul 10:24
#Second Usage Life(filename) — reading in file done 22-Jul-2015 10:59
#Third Usage Life(filename) — including displaying the file and dummy next generation 22-Jul-2015 18:34
#Fourth Usage Life(filename) — real next generation done 22-Jul-2015 20:17
#Fifth Usage Life(filename, generations to calculate, every how many generations to display) done 23-Jul-2015 20:44
#NOTE: Decided not to implement writing single generations to separate files. Seems unnecessary and would just fill my disk with lots of files.
#NOTE: Piping console output to text file is fine. Modern text editors can open large, multi-generation Life files and navigate quickly
#TODO: Next learning topics (backlog) would be:
#….1. Graphic display
#….2. Mouse input of boards
#….3. Change to argparse for robust command line parsing
#….4. Clean up file layout according to some accepted Python style guide
#….5. Refactor to be more functional as opposed to procedural, and reduce global variable use, if that makes code clearer

#Create a single Life instance
myLife = Life()

#Get the command line arguments
#TODO:Switch to Python argparse module instead

#From first command line argument, get pathname of initial (zero’th) generation text file
initialGenerationFilename = sys.argv[1]

#From second command line argument, get number of generations to calculate
numGenerations = int(sys.argv[2])

#From third command line argument, get period: every how many generations to display or write to file
#NOTE: currently, writing results to a file is not yet included
periodOfDisplay = int(sys.argv[3])

#Read in the initial (zero’th) generation board from text file

#Display initial (zero’th) generation
print(“Generation #”,0)
myLife.displayBoard()
print()

#Generate and display following generations
for generationIndex in range(1,numGenerations+1): #NOTE:Add 1 because range end is always one less than value
….myLife.calculateNextGeneration()
….myLife.makeBoardCurrent()
….if (generationIndex % periodOfDisplay) == 0: #NOTE: “%” is Python modulus — checking for evening divisible by period of display
……..print(“Generation #”,generationIndex)
……..myLife.displayBoard()
……..print()

And here’s some output. The 4-generation-period “glider” on a 25 x 25 grid, starts its flight from upper-left towards lower-right.
The command I ran was

Life.py Glider25by25.txt 8 4

and the output was
``` Generation # 0 ..X...................... ...X..................... .XXX..................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... .........................```

```Generation # 4 ......................... ...X..................... ....X.................... ..XXX.................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ```
```Generation # 8 ......................... ......................... ....X.................... .....X................... ...XXX................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... ......................... .........................```

Written by Tom Harris

July 24, 2015 at 12:48 am

Learning a New Programming Language, with Life (part 2)

with one comment

I started learning Python two days ago, as described in part 1.

Yesterday I learned about Python Classes and Functions, including class and instance variables, and the special __init__ function. Today I learned enumerate, and the list function rstrip. I also learned not to forget the colon (:) in def, for, if, and else constructs!

After just a few hours today, my first project has actually started working.

For one “generation”, according to the rules of Conway’s Game of Life.

Here it is, complete with TEST comments preceding debugging code, and TODO comments for what’s next.

`#Life.py`

```#Implementation of Conway's Game of Life #See https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life```

#Tom Harris 22-Jul-2015

#Import libraries needed
import sys #for command line argument support

#Global functions
def initialGenerationFilename():
….#Get pathname from first command line argument
….return sys.argv[1]

#Define the Life class

class Life:
….#class attributes
….#None

….#class methods

….def __init__(self):
……..#instance attributes: define and initialize (some more later after reading in file)
……..self.mainBoard = [] #The board of cells
……..self.workingBoard = [] #A work area for creating the next Life generation
….
……..#Open the file
……..initialGenerationFile = open(filename)

……..#Read from the file, line by line:
……..for line in initialGenerationFile:
…………listLine = list(line.rstrip(‘\r\n’))
…………self.mainBoard.append(listLine) #add the line, in Python list format, to the mainBoard

……..#Determine dimensions of the board after reading in
……..#WARNING: dimensions are 1 larger than largest index, since indices start at 0
……..self.maxRowMain = len(self.mainBoard)
……..self.maxColMain = len(self.mainBoard[0])

……..#Close the file
……..initialGenerationFile.close

……..return self.mainBoard

….def displayBoard(self):
……..for rowIndex, row in enumerate(self.mainBoard):

…………for colIndex, col in enumerate(row):
…………….if self.mainBoard[rowIndex][colIndex]== “1”:
………………..print(“*”, end=”)
…………….else:
………………..print(“.”, end=”)

…………print() #print newline at end of each row

……..return 0 #TODO: replace with returning a result — what do we do with it? This is a function, not a procedure.

….def makeBoardCurrent(self): #TODO: Think of a better name — means to copy from the next-generation workingBoard to mainBoard
……..self.mainBoard = self.workingBoard
……..
……..return self.mainBoard

….def calculateNextGeneration(self):

……..#Calculate next generation on workingBoard based on mainBoard
……..workingRow = [] #for building up workingBoard row by row

……..#Calculate next generation on workingBoard based on mainBoard
……..for mainRowIndex, mainRow in enumerate(self.mainBoard):
…………for mainColIndex, mainElement in enumerate(mainRow):

…………….#Restart count of live cells
…………….numLiveCells = 0

…………….for i in [-1, 0, 1]:
………………..#Check row before, same row, next row
………………..rowOfCellToCheck = mainRowIndex + i

………………..#Wraparound
………………..if rowOfCellToCheck < 0:
……………………rowOfCellToCheck = self.maxRowMain – 1

………………..if rowOfCellToCheck > (self.maxRowMain – 1):
……………………rowOfCellToCheck = 0
……………………
………………..for j in [-1, 0, 1]:
……………………#Check column before, same column, next column
……………………colOfCellToCheck = mainColIndex + j

……………………#Wraparound
……………………if colOfCellToCheck < 0:
……………………….colOfCellToCheck = self.maxColMain – 1

……………………if colOfCellToCheck > (self.maxColMain -1):
……………………….colOfCellToCheck = 0

……………………#If cell to check is alive, increment count of live cells
……………………#But if same row and column, ignore
……………………sameColAndRow = abs(i) + abs(j)
……………………if sameColAndRow != 0:
……………………….if self.mainBoard[rowOfCellToCheck][colOfCellToCheck] == “1”:
………………………. numLiveCells = numLiveCells + 1
…………………………………………

…………….if self.mainBoard[mainRowIndex][mainColIndex] == “1”: # The survival rules

………………..if 2 <= numLiveCells <= 3:
……………………workingRow.append(‘1’)
………………..else:
……………………workingRow.append(‘0’)

…………….else: #The reproduction rule

………………..if numLiveCells == 3:
……………………workingRow.append(‘1’)
………………..else:
……………………workingRow.append(‘0’)
……………………
…………self.workingBoard.append(workingRow)
…………workingRow = []
………………..
……..return self.workingBoard

#The actual main Life program
#Usage will be Life(filename, generations to calculate, every how many generations to display, display yes/no, write to files yes/no)
#First Usage Life() — done 22-Jul 10:24
#Second Usage Life(filename) — reading in file done 22-Jul-2015 10:59
#Third Usage Life(filename) — including displaying the file and dummy next generation 22-Jul-2015 18:34
#Fourth Usage Life(filename) — real next generation done 22-Jul-2015 20:17
#TODO: Fifth Usage Life(filename, generations to calculate, every how many generations to display)

#Create a single Life instance
myLife = Life()

#Run the methods in the right order
myLife.displayBoard() #TEST: display the mainBoard before generating
print()
myLife.calculateNextGeneration()
myLife.makeBoardCurrent()
myLife.displayBoard()

And here’s the output. It’s the simplest oscillator — the “Blinker”, one generation, with wraparound:

```........ *....... *....... *....... ........ ........ ........ ........ ........```

```........ ........ **.....* ........ ........ ........ ........ ........ ........```

Written by Tom Harris

July 22, 2015 at 8:48 pm

Learning a New Programming Language, with Life

For an upcoming course to help science students master the computer as a tool for their work, by learning to code, I need to learn Python. I reviewed discussions on Python 2 vs Python 3, and chose the latter.

To operate in a new language, I only need to learn and practice four things:

1. Input
2. Storage and Retrieval
3. Processing
4. Output

(Wish I could program functionally, and avoid #2. But Python isn’t designed for functional programming.)

My traditional project for learning a new language is to code Conway’s Game of Life.

After my first 2 hours in Python, with Python 3.4.3 installed, and Google for answering my questions, I’ve covered command line arguments (though still without argparse for user error handling), input from text file, output to console, line-by-line storage, and character-by-character retrieval from a Python list.

I’ve also imported one Python module (sys), and gotten used to code blocks via indentation.

Here’s my code so far. Wish me luck as I continue learning!

`#ReadTextFromFileAndDisplay.py`

``` #Import libraries needed import sys #for command line argument support #Get pathname of file to open from first command line argument filenameToOpen = sys.argv[1] #Open the file myFile = open(filenameToOpen) print("Pathname was: ", filenameToOpen) print("Result code for opening the file was: \n", myFile) #Read and display entire file #Also store it in a list myList = [] #declare list, initially empty print("\nHere is the file as it's being read in: \n") #Read from the file, line by line: for line in myFile: ....print(line, end='') #display each line ....myList.append(line) #add the line to the list #Close the file myFile.close print("\nFile should be closed now.\n") print("Here's the contents of the list... \n") ```

```#Display the contents of the list for row in myList: ....print(row, end='') #entire rows ....print() ....for iCharacterIndex in range(len(row)): ........print(row[iCharacterIndex]) #character by character```

p.s. I’ve put periods in to show the indentation, since it’s hard to get WordPress to preserve whitespace.

Written by Tom Harris

July 20, 2015 at 7:44 pm