Friday, September 18, 2020

Lesson 2-3: If-then Statements in Computer Programs (Day 23)

 I've written so much on the blog about the reopening plans in my new district. I've also, for the sake of comparison, looked at reopening plans for schools around the state and country. Indeed, here's an Edsource link about reopening plans across California:

https://edsource.org/2020/families-face-the-demands-of-online-and-hybrid-learning/639224

But I haven't said too much about my old district and its reopening plans recently. I mentioned it a little in late July, then whole a post on it on that district's first day of school -- only to edit it out and only write about my new district instead. I also changed the blog calendar so that it matches my new district rather than my old one. So what's going on in my old district? Is it still possible for me to sub there?

Well first of all, that district is in LA County. This county is still stuck on the purple list with no signs of it becoming red soon. As long as the county is purple, the district must remain online only -- this is considered Stage 1 of a four-stage coronavirus plan.

Also, I found out today that, while there are subs working at each school during Stage 1, I personally won't be subbing there. That's because apparently, there's a list of pre-approved subs at each site, and only those subs will get called. Since I obviously haven't been notified by any school that I'm a pre-approved sub, the assumption is that I haven't been so chosen. Therefore I conclude that I won't get any calls from this district in Stage 1.

Recall that Stage 2 in this district consists of "office hours," where some students attend certain classes in person for a half hour at a time. (Stage 3 is what we would normally think of as "hybrid.") Anyway, I have no idea whether it's possible for me to get calls in Stage 2 -- you'd think so, but I wouldn't be surprised if schools just stick to their pre-approved lists. And as far as I know, LA County won't get off the purple list at all, so the school can't progress beyond Stage 1 (much less Stage 2) at all -- and this would mean that I won't sub at all in this district the entire year.

On one hand, this shouldn't matter since I already have my new district where I've received several subbing calls this week. On the other hand, in my life I've experienced what I call the "five-year itch" -- I know, it's supposed to be seven years, but for me it's five.

You see, in my entire life I've never held a job for more than five years. It all started with my first job working part-time at the UCLA Library. I did say recently the exact date of my first day on the job -- it was November 3rd, 1999. My last day on the job was January 7th, 2004 -- mainly because only current tuition-paying students were allowed to have that job. I completed my Masters Degree during Fall Quarter 2003, and I was allowed to stay until the next quarter began -- Winter Quarter started on January 8th that year, so my last day was January 7th.

If I had pursued a doctoral degree and made it to my fifth anniversary in November 2004, then I would have completed five full years. As it stands, I was only there for four years and two months Since then I've had a number of part-time jobs that I left in order to pursue the next step in getting my credential.

I first worked in my current old district on October 22nd, 2014 -- I know the exact date because I blogged about it that day. You might notice that this date was more than five years ago. But I didn't work continuously at this job because I left to teach at the charter school. Thus the time I've worked at my district so far must be somewhat less than five years.

Counting years of service at a school is tricky due to summer break. For example, a teacher who works from the first day of school in her first year to the last day of school in fifth year will normally describe herself as having worked five years at her school, but technically she only worked from August of the first year to June of the fifth year, so that's only four years and ten months. But we should count it as five full years because we know that schools are closed for the summer.

But I can't justify counting an October start as a full year. Thus I must sub a little this year in order to break my "five-year itch.". It's easiest just to say that I must make it to my sixth anniversary in the district, October 22nd -- that's six years minus one year at the charter, so it's five years. If I sub in my district on or after that date, then my itch will finally be scratched.

So if the schools remained closed and I can't sub at all this year due to not being on this pre-approved list, I would have to backdate my last day of work to March 10th, when I last subbed here. So on my resume, I'd have to write:

[insert LA County district name here] School District. Substitute Teacher, October 2014-June 2016, August 2017-March 2020. Reason for leaving is coronavirus.

So that's three full school years (2015-2016, 2017-2018, 2018-2019), plus about three quarters each during the 2014-2015 and 2019-2020 school years. So we can justifiably round this off to 18 quarters or nine semesters, but not five years (or ten semesters). If I can sub here on October 22nd, then I can change the ending date from March 2020 to October 2020 and scratch the five-year itch.

(Indeed, this is exactly what happened with my old tutoring job. I last tutored in June 2015 and didn't get work over the summer. In the fall, I was told that I wasn't needed to tutor any longer -- but I list my exit date on my resume as June 2015.)

Here's my plan for this district -- if it reaches Stage 2 and I still can't be called because I'm not on a certain list, then it will be time for me to contact the district to figure out how to get on the list. I wonder whether any of these two or three pre-approved subs per school have a math background -- if not, then perhaps I can convince that school to approve me for math jobs.

Since I won't be subbing here during Stage 1, I don't have inside info regarding the upcoming hybrid stages -- nor does the district have to publish them until LA County is red or better. So I can't say whether the hybrid will be concurrent or not, or whether Cohorts A and B will be alphabetical by last name or based on something else.

OK, that's enough about my old district. In my new district, today is Day 23, approximately the end of the first quaver. Originally, hybrid would be starting next week, until Orange County had to spend an extra week on the purple list. Instead, we know that my new district will starting its own hybrid schedule on September 29th, and as expected, many subs will be needed. In fact, the district is already assigning us subs to work at specific schools during that entire week.

Lesson 2-3 of the U of Chicago text is called "If-then Statements in Computer Programs." It does not appear in the modern Third Edition of the U of Chicago text, and we can see why when we look a little at what I wrote last year on the subject:

Lesson 2-3 of the U of Chicago text discusses computer programs written in BASIC. We must remind ourselves that this book was written over 20 years ago, back when BASIC was a popular language. But now, BASIC has been derided as "spaghetti code" and isn't as widely used anymore.

And so the Third Edition avoids this issue by leaving out this lesson entirely. Two years ago, I changed the lesson to use TI-BASIC, the language of the TI graphing calculator, instead. But today I have do access to something that runs BASIC programs -- the emulator of my old 1980's computer:

http://www.haplessgenius.com/mocha/

Whereas human languages such as English are illogical, computer languages are supposed to be logical.

So for today, I'll post the old TI-BASIC worksheets from last year. But I'm actually going to write about BASIC itself using the emulator.

Let's start by writing the four BASIC programs used in the U of Chicago text. All four of these programs run on the emulator:

Program #1: Number of Diagonals in Polygon

10 PRINT "COMPUTE NUMBER OF DIAGONALS IN POLYGON"
20 PRINT "ENTER THE NUMBER OF SIDES"
30 INPUT N
40 IF N>=3 THEN PRINT "THE NUMBER OF DIAGONALS IS "; N*(N-3)/2
50 END

Program #2: Inventory of Electro-Robots

10 PRINT "INVENTORY OF ELECTRO-ROBOTS"
15 PRINT "ENTER THE NUMBER OF ELECTRO-ROBOTS"
20 INPUT N
25 PRINT "PROFIT EQUALS";5*N-1500
30 IF N<300 THEN PRINT "ORDER MORE ELECTRO-ROBOTS."
35 END

Program #3: Area of Circle

10 INPUT R
20 LET A=3.14159*R*R
30 IF R>0 THEN PRINT R,A
40 END

Program #4: Diagonals in Many Polygons

10 PRINT "NUMBER OF DIAGONALS IN POLYGONS"
20 FOR N=3 TO 20
30 PRINT "THE NUMBER OF SIDES IS";N
40 PRINT "THE NUMBER OF DIAGONALS IS";N*(N-3)/2
50 NEXT N
60 END

All of these programs work on the emulator. Actually, my computer doesn't require the last line of the program to be END -- we could omit each of those final lines. Also, my computer doesn't require line 20 of Program #3 to begin with LET -- just 20 A=3.14159*R*R is sufficient. Don't forget to type in NEW to erase the old program before entering the new one.

Now let's write some programs of our own. Earlier when we were reading Ogilvy, there was mention of a GCD algorithm. Let's try to write it in BASIC.

The main problem here is that BASIC doesn't do long division with quotient and remainder -- instead it only does decimal division (or "floating-point division"). Remainders, as we see above, are critical to the algorithm. But it's possible -- though not easy -- to use floating-point division for remainders.

Let's go back to the example above -- the GCD of 30 and 108. We need the remainder of 108/30, but if we ask BASIC to find 108/30, it calculates it as 3.6. We need to convert 3.6 into a remainder.

The key function is the INT function. INT represents the ceiling function in mathematics (as in the greatest-integer function). So INT(3.6)=3. Well, this gives us the quotient, but we need the remainder.

Hmmm, the part that relates to the remainder is the fraction part. We can find this by subtracting the integer part from the original number: 3.6-INT(3.6)=.6. This fractional part is 18/30, so we must multiply it back by 30 to obtain the remainder -- 18.

Theoretically, this should work. The problem is that all this division produces rounding errors -- and such rounding can lead to incorrect answers. For example, let's go back to Program #3 that calculates the area of a circle. If we try entering a radius of 4 or 5, there is a rounding error that becomes obvious after trying a radius of 1, 2, or 3 first:

r     Area (as given by emulator)
1    3.14159
2    12.56636
3    28.27431
4    50.2654401
5    78.5397501

That "01" at the end of the areas for 4 and 5 stick out like a sore thumb. And no, the "01" has nothing to do with using pi to more accuracy, since it would be 50.2654825 instead (and besides, we never gave the program those extra digits of pi).

The reason for the inaccuracy is that computers work in binary, not decimal. The decimal 3.14159 actually means 3+14159/100000, but fractions with a denominator of 100000 (or anything other than a power of two) can't be expressed exactly in binary. The difference between 3.14159 and whatever binary fraction the computer uses is small enough not to appear when multiplying by 4 or 9, but when we multiply by 16 or 25 it appears. This problem never occurs if we only use binary fractions such as .5, .125, and so on.

And this is just multiplication -- in division, we're even more likely to have rounding errors. We know that fractions like 1/3 trip us up in both decimal and binary. Here, we must multiply .6 by 30 to obtain our remainder 18, but .6 isn't a binary fraction. Therefore, we'll obtain something like 17.9999999 or 18.0000001 instead of 18 as the remainder. This will trip up the algorithm -- it's supposed to stop when the remainder is zero, but it'll never reach zero if we have 17.9999999 or 18.0000001.

Using INT will work if we have 18.0000001, but INT(17.9999999)=17 instead of the desired 18. To solve this problem, we implement 4/5 rounding by adding .5 to the number before taking INT. This works, since both INT(18.4999999) and INT(18.5000001) are equal to 18.

Here is our GCD program, written in BASIC:

10 PRINT "INPUT TWO NUMBERS"
20 INPUT A,B
30 IF B=0 THEN PRINT "THE GCD IS";A: END
40 Q=A/B
50 R=INT((Q-INT(Q))*B+.5)
60 A=B
70 B=R
80 GOTO 30

You may notice a few things about how this program is written:

  • The program doesn't end with END. In fact, END appears at the end of line 30, because we really want the program to end after printing the GCF rather than proceed to line 40.
  • LET is omitted, as my computer allows.
  • INPUT A,B allows us to enter both numbers using a comma.
  • The strange part is that the IF statement (which is the subject of Lesson 2-3) appears near the start of the program. Actually, most GCD programs are written this way, in order to safeguard against the user entering 0 as one of the numbers. The program ends before reaching line 40 and thus avoids division by zero.
  • After the division, if the remainder R=0, then the final answer is in B. But the values in B and R have moved to A and B before we reach the GOTO and IF statements. Therefore B is the value that we test for zero, and A contains the final answer.

Notice that in TI-BASIC, we have a fPart function that gives us the fractional part directly without needing int. To avoid rounding error, we use the round function -- 0 here means no decimal places (nearest integer):

PROGRAM:GCD
:Disp "2 NUMBERS"
:Input A
:Input B
:While B
:round(fPart(A/B)B,0)->R
:B->A
:R->B
:End
:A

In this program, both Goto and If are avoided because we used a While loop instead. The While loop does the testing -- the loop is executed while B is true (nonzero) and stops when it is false (0). Here the word End ends the While loop, not the program.

In other languages, such as C++, there is a % operator that gives us remainder directly. Here is how we'd write the gcd function:

int gcd(int a, int b) {return b ? gcd(b, a%b) : a;}

Here we replaced "if" with the ternary operator ?: -- it means "If b is true (nonzero), then recursively call gcd on b and a%b (the remainder), else the answer is a."

2020 update: The preceding line should work in Java as well, now that I'm learning Java.

In fact, I've been thinking about computers lately with all my Zoom problems. One problem with error messages -- whether it's BASIC, Java, or Zoom -- is that it's so easy to misdiagnose -- for example, the rounding errors in BASIC. When we see that error message from yesterday:

"Waiting for the host to start this meeting. This is a recurring meeting. If you are the host, sign in to start this meeting."

it's easy to assume that it's something wrong that the regular teacher (the original host) did or didn't do. But based on the times when I've seen and haven't seen the message, I now see that it has nothing to with the regular teacher -- my problems are being misdiagnosed. If only someone could correctly diagnose what I'm doing wrong, it would have been fixed by now. I'm still worried about these Zoom problems as we approach concurrent hybrid.

OK, let's get back to BASIC. Now that I have the emulator, I can't help but try to play microtonal music using the SOUND command in BASIC.

In an earlier post, we discovered that the most important number when making music using SOUND on the computer is 261. As it turns out, SOUND is based on EDL, or equal divisions of length, where the length of the string is the number of steps it takes to get to 261, the "bridge." For example, Sound 61 is 200 steps away from the bridge, while Sound 161 is 100 steps away from the bridge. Thus the interval between Sounds 61 and 161 is 200/100 or 2/1, which is an octave. (To make it easier to understand, the word "Sound" refers to the actual number used in the SOUND command, while "Degree" refers to the number of steps from away from Bridge 261.)

Obviously, in past years I used today's BASIC post as an excuse to go back to Mocha music written in BASIC.

10 INPUT "EDO";N
20 DIM R(N), D(N)
30 FOR S=0 TO N
40 R(S)=2^(S/N)
50 NEXT S
60 M=N
70 FOR L=12 TO 260 STEP 2
80 PRINT "TESTING EDL";L
90 E=0
100 FOR S=0 TO N
110 D=L/R(S)
120 E=E+ABS(D-INT(D+.5))
130 NEXT S
140 IF E>=M THEN 190
150 FOR S=0 TO N
160 D(S)=INT(L/R(S)+.5)
170 NEXT S
180 M=E
190 NEXT L
200 CLS
210 PRINT "PLAYING SCALE"
220 FOR S=0 TO N
230 PRINT "DEGREE";D(S);"SOUND";261-D(S)
240 SOUND 261-D(S),4
250 NEXT S
261 END

(Note: Line 40 requires an up-arrow where you see the ^ caret.)

Here's how the program works -- the user inputs the desired EDO in line 10. Lines 30-50 set up the correct ratios for your EDO -- so if you choose 12EDO, then the ratios are 2^(0/12), 2^(1/12), 2^(2/12), all the way up to 2^(12/12).

Lines 70-190 set up a loop to test every EDL, from the shortest (12EDL) to the longest (260EDL). It calculates the error E between the correct ratio and closest possible degree available (notice how INT(D+.5) is used to round D to the nearest degree). The IF statement in line 140 is used to see whether E is less than the minimum error M encountered so far. If E>=M, then we skip to line 190 and check the next EDL. If E<M, then we have a new "best EDL so far," and lines 150-170 set up a loop to replace the list of "degrees of the best EDL so far" with the new list.

Afterwards, the final loop from lines 220-250 actually play the best scale on the computer. (Don't forget to click the "Sound" box in order to hear the sound!) I decided to make the length 4 because just as the magic number for pitch is 261, the magic number for duration is 32 (whole note). As today's the start of the second quaver, let's make it play quavers, so 32/8 = 4. The final END line isn't needed, but I include it just as an excuse to honor Bridge 261 with a line 261.

Here are the best possible scales the computer found for some EDO's:

5EDO: use 256EDL (Degrees 256, 223, 194, 169, 147, 128 -- Sounds 5, 38, 67, 92, 114, 133)
7EDO: use 156EDL (Degrees 156, 141, 128, 116, 105, 95, 86, 78)
10EDO: use 256EDL (add Degrees 239, 208, 181, 158, 137 in between 5EDO)
12EDO: use 232EDL (Degrees 232, 219, 207, 195, 184, 174, 164, 155, 146, 138, 130, 123, 116)
16EDO: use 214EDL (Degrees 214, 205, 196, 188, 180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107)

I actually recommend not going past 12EDO, since the error E rises sharply. If you must try a higher EDO, then use 16EDO as it has the least error. Other multiples of 4 (20EDO, 24EDO, 28EDO) have lower errors for their size, while 22EDO and 31EDO (mentioned in earlier posts) also have lower errors for their size. Beyond 31EDO, all scales are about equally bad (with the odd EDO's marginally better than the even EDO's).

It's interesting that 232EDL is the best for 12EDO, since the EDL link I mentioned in an earlier post recommended 120EDL and 196EDL (among others) to make a 12EDO scale.

But to me, making EDO scales is such a waste. The computer uses EDL's, which are based on exact divisions of length, so we should be playing sounds in just intonation, not EDO's.


No comments:

Post a Comment