CS3343/3341
 Analysis of Algorithms 
Spring 2012
Weird Topic
  Balanced Ternary   


Three-state Hardware:

Most of our hardware has been based on binary state storage, and binary numbers were used internally. Hardware involved with commercial transactions often has to handle money exact to the penny, but even here the number of pennies can be represented in binary. However, a subset of hardware was constructed to handle decimal numbers directly -- handheld calculators are one good example. From the beginning they used decimal numbers internally, so that the limit on the size of an exponent might be +99, for example. This web page asks the question: what if, instead of flip-flop hardware, we had flip-flap-flop hardware, where a 3-state storage was natural for the hardware? Most people would assume that we might then use base 3 numbers, ternary it's called. But there is a startling alternative.


Base Three Numbers:

Of course we could deal with numbers using 3 as the base. Then the digits (or "trits") would be 0, 1, or 2. The number in the rightmost place would be multiplied by 30 = 1, the next over by 31 = 3, and then onto 32 = 9, 33 = 27, 34 = 81, 35 = 243, and so on. Thus the base 3 number 210213 would equal:

The page on elementary recursion gives code that will transform a number into base 3.


Balanced Ternary:

There is another number system, similar to base 3, that Knuth (Vol. 2, p. 207) calls "perhaps the prettiest number system of all." Instead of 0, 1, and 2, this new system uses -1, 0, and 1 for the value of "trits." Just as with hexadecimal numbers, where one has to invent new symbols for the "digits" with values from 10 to 15 (using A through F), here one needs at least a new symbol for -1. There is no standard notation, but the table shows two ways to write -1: use 1, or -. It's not crazy to use a "minus" sign to stand for -1 because balanced ternary represents negative numbers without an initial minus sign (see below).

These examples include ones with a "decimal" point, which is not handled by the software here. (Several items in this writeup are taken verbatim from Knuth.)

Balanced Ternary, Examples and Notation
Values -1 0 1 Samples
5- 632 5/9- 32 and 5 / 9 1 / 2
Wikipedia-0++ - - - + 0+ + - 0 . - -- - + 0 . + + 0 . + + + + + ...
Knuth
1
01 1 1 1 1 1 0 1 1 1 0 . 1 1 1 1 1 0 . 1 1 0 . 1 1 1 1 1 ...
My own-0|| - - - | 0| | - 0 . - -- - | 0 . | | 0 . | | | | | ...

In the example of -6 above, -6 = -1*32 + 1*31 + 0*30 = -9 + 3 + 0 = -6

Balenced ternary has significant advantages over other number systems:

The programs below carry out conversions from an int in a program to base 3 and then to balanced ternary, building a table of values. This program uses Knuth's method for transforming ternary to balanced ternary:

Start with the number:

(208.3)10 = (21201.02200220022...)3

Then add the "infinite" number ...111111.111111...3, carrying digits where necessary (2+1 = 0 carry 1) to get:

(...111111210012.21012101210...)3

Finally, subtract ...111111.111111...3 by decrementing each digit, to get, in Knuth's notation.

(208.3)10 =
(21201.02200220022...)3
   (base three notation) =
101101.101010101010...    (Knuth's notation) =
|0--0|.|0-0|0-0|0-0 ...    (my notation)

This example illustrates digits to the right of a decimal, which we don't cover in the program below.

Balanced Ternary Values
Java C
// BalancedTernary.java: convert to balanced ternary
//   with weights ("trits" -1, 0, and 1
//   least sig trit in 0th postion
public class BalancedTernary { 
   private int len; // length of arrays to use
   public int[] zeros; // zero value
   public BalancedTernary(int leng) {
      len = leng;
      zeros = new int[len];
      for (int i = 0; i < len; i++) zeros[i] = 0;
   }
   // convert to base 3
   public int[] toBase3(int n) {
      int[] r = new int[len];
      for (int i = 0; i < r.length; i++) r[i] = 0;
      int loc = 0;
      while (n > 0) {
         r[loc++] = n%3;
         if (loc == r.length) {
            System.out.println("Overflow in toBase");
            System.exit(1);
         }
         n = n/3;
      }
      return r;
   }

   // find negative of bal ternary #
   public int[] negate(int[] b) {
      int[] c = new int[b.length];
      for (int i = 0; i < b.length; i++) c[i] = b[i];
      for (int i = 0; i < c.length; i++) {
         if (c[i] == 1) c[i] = -1;
         else if (c[i] == -1) c[i] = 1;
      }
      return c;
   }


   // convert from base 3 to bal ternary
   public int[] toBalTern(int[] r) {
      int[] b = new int[r.length];

      for (int i = 0; i < r.length; i++) b[i] = r[i];
      for (int i = 0; i < r.length -1; i++) {
         // add 1's to all but most sig digit
         b[i] = b[i] + 1;
         // process carry
         int j = i;
         while (b[j] == 3) {
            b[j] = 0;
            j++;
            b[j] = b[j] + 1;
         }
      }
      // subtract 1 from all but most sig digit
      for (int i = 0; i < r.length -1; i++)
         b[i] = b[i] - 1;
      return b;
   }

   // print in reverse without leading zeros
   public void printReverseNoZero(int[] r) {
      int j = r.length-1;

      while (r[j] == 0) j--;
      for (int i = j; i >= 0; i--)
         System.out.print(r[i]);
   }

   // print bal ternary with | - and 0
   public void printBalTern(int[] b) {

      for (int i = b.length-1; i >= 0; i--) {
         if (b[i] == 1) System.out.print("|");
         else if (b[i] == -1) System.out.print("-");
         else System.out.print("0");
      }
   }

   // print bal ternary without leading zeros
   public void printBalTernNoZero(int[] b) {
      int j = b.length-1;
      while (b[j] == 0) j--;

      for (int i = j; i >= 0; i--) {
         if (b[i] == 1) System.out.print("|");
         else if (b[i] == -1) System.out.print("-");
         else System.out.print("0");
      }
   }

   public void printHeader() {
      System.out.println("             \tbalanced");
      System.out.println("    n   base 3\tternary");
      System.out.println("-----------------------");
   }

   public static void main(String[] args) {
      BalancedTernary balT = new BalancedTernary(10);

      // print table of values
      balT.printHeader();
      System.out.println("    0:   0   \t0");
      for (int i = -1; i >= -40; i--) {
         if (i >= -9) System.out.print("   ");
         else System.out.print("  ");
         System.out.print(i + ":  -");
         int[] x = balT.toBase3(-i);
         balT.printReverseNoZero(x);
         System.out.print(" \t");
         int[] y = balT.toBalTern(x);
         balT.printBalTernNoZero(balT.negate(y));
         System.out.println();
      }
      System.out.println();

      balT.printHeader();
      System.out.println("    0:   0   \t0");
      for (int i = 1; i <= 40; i++) {
         if (i < 10) System.out.print("    ");
         else if (i < 100) System.out.print("   ");
         System.out.print(i + ":   ");
         int[] x = balT.toBase3(i);
         balT.printReverseNoZero(x);
         System.out.print(" \t");
         int[] y = balT.toBalTern(x);
         balT.printBalTernNoZero(y);
         System.out.println();
      }
   }
}
// BalancedTernary.c: convert to bal tern
//   with weights ("trits" -1, 0, and 1
//   least sig trit in 0th postion
#include <stdio.h>
#include <stdlib.h>
#define LEN 10 // length of arrays to use

int zeros[LEN]; // zero value


// convert to base 3
int* tobase3(int n) {
   int* r = (int *)malloc(4*LEN);
   int i;
   for (i = 0; i < LEN; i++) r[i] = 0;
   int loc = 0;
   while (n > 0) {
      r[loc++] = n%3;
      if (loc == LEN) {
         printf("Overflow in toBase\n");
         exit(1);
      }
      n = n/3;
   }
   return r;
}

// find negative of bal ternary #
int* negate(int* b) {
   int* c = (int *)malloc(4*LEN);
   int i;
   for (i = 0; i < LEN; i++) c[i] = b[i];
   for (i = 0; i < LEN; i++) {
      if (c[i] == 1) c[i] = -1;
      else if (c[i] == -1) c[i] = 1;
   }
   return c;
}

// convert from base 3 to bal ternary
int* tobaltern(int* r) {
   int* b = (int *)malloc(4*LEN);
   int i;
   for (i = 0; i < LEN; i++) b[i] = r[i];
   for (i = 0; i < LEN -1; i++) {
      // add 1's to all but most sig dig
      b[i] = b[i] + 1;
      // process carry
      int j = i;
      while (b[j] == 3) {
         b[j] = 0;
         j++;
         b[j] = b[j] + 1;
      }
   }
   // subtract 1 from all but most sig dig
   for (i = 0; i < LEN -1; i++)
      b[i] = b[i] - 1;
   return b;
}

// print in reverse without leading zeros
void printreversenozero(int* r) {
   int j = LEN-1;
   int i;
   while (r[j] == 0) j--;
   for (i = j; i >= 0; i--)
      printf("%i", r[i]);
}

// print bal ternary with | - and 0
void printbaltern(int* b) {
   int i;
   for (i = LEN-1; i >= 0; i--) {
      if (b[i] == 1) printf("|");
      else if (b[i] == -1) printf("-");
      else printf("0");
   }
}

// print bal ternary without leading zeros
void printbalternnozero(int* b) {
   int j = LEN-1;
   while (b[j] == 0) j--;
      int i;
      for (i = j; i >= 0; i--) {
         if (b[i] == 1) printf("|");
         else if (b[i] == -1) printf("-");
         else printf("0");
   }
}

void printheader() {
   printf("             \tbalanced\n");
   printf("    n   base 3\tternary\n");
   printf("--------------------------\n");
}

int main() {
   int i;
   for (i = 0; i < LEN; i++) zeros[i] = 0;
   // print table of values
   printheader();
   printf("    0:   0   \t0\n");
   for (i = -1; i >= -40; i--) {
      if (i >= -9) printf("   ");
      else printf("  ");
      printf("%i:  -", i);
      int* x = tobase3(-i);
      printreversenozero(x);
      printf(" \t");
      int* y = tobaltern(x);
      printbalternnozero(negate(y));
      printf("\n");
   }
   printf("\n");

   printheader();
   printf("    0:   0   \t0\n");
   for (i = 1; i <= 40; i++) {
      if (i < 10) printf("    ");
      else if (i < 100) printf("   ");
      printf("%i:   ", i);
      int* x = tobase3(i);
      printreversenozero(x);
      printf(" \t");
      int* y = tobaltern(x);
      printbalternnozero(y);
      printf("\n");
   }
}
               balanced
    n   base 3  ternary

0: 0 0 -1: -1 - -2: -2 -| -3: -10 -0 -4: -11 -- -5: -12 -|| -6: -20 -|0 -7: -21 -|- -8: -22 -0| -9: -100 -00 -10: -101 -0- -11: -102 --| -12: -110 --0 -13: -111 --- -14: -112 -||| -15: -120 -||0 -16: -121 -||- -17: -122 -|0| -18: -200 -|00 -19: -201 -|0- -20: -202 -|-| -21: -210 -|-0 -22: -211 -|-- -23: -212 -0|| -24: -220 -0|0 -25: -221 -0|- -26: -222 -00| -27: -1000 -000 -28: -1001 -00- -29: -1002 -0-| -30: -1010 -0-0 -31: -1011 -0-- -32: -1012 --|| -33: -1020 --|0 -34: -1021 --|- -35: -1022 --0| -36: -1100 --00 -37: -1101 --0- -38: -1102 ---| -39: -1110 ---0 -40: -1111 ----
               balanced
    n   base 3  ternary

0: 0 0 1: 1 | 2: 2 |- 3: 10 |0 4: 11 || 5: 12 |-- 6: 20 |-0 7: 21 |-| 8: 22 |0- 9: 100 |00 10: 101 |0| 11: 102 ||- 12: 110 ||0 13: 111 ||| 14: 112 |--- 15: 120 |--0 16: 121 |--| 17: 122 |-0- 18: 200 |-00 19: 201 |-0| 20: 202 |-|- 21: 210 |-|0 22: 211 |-|| 23: 212 |0-- 24: 220 |0-0 25: 221 |0-| 26: 222 |00- 27: 1000 |000 28: 1001 |00| 29: 1002 |0|- 30: 1010 |0|0 31: 1011 |0|| 32: 1012 ||-- 33: 1020 ||-0 34: 1021 ||-| 35: 1022 ||0- 36: 1100 ||00 37: 1101 ||0| 38: 1102 |||- 39: 1110 |||0 40: 1111 ||||
               balanced
    n   base 3  ternary

-41: -1112 -|||| -42: -1120 -|||0 -43: -1121 -|||- -44: -1122 -||0| -45: -1200 -||00 -46: -1201 -||0- -47: -1202 -||-| -48: -1210 -||-0 -49: -1211 -||-- -50: -1212 -|0|| -51: -1220 -|0|0 -52: -1221 -|0|- -53: -1222 -|00| -54: -2000 -|000 -55: -2001 -|00- -56: -2002 -|0-| -57: -2010 -|0-0 -58: -2011 -|0-- -59: -2012 -|-|| -60: -2020 -|-|0 -61: -2021 -|-|- -62: -2022 -|-0| -63: -2100 -|-00 -64: -2101 -|-0- -65: -2102 -|--| -66: -2110 -|--0 -67: -2111 -|--- -68: -2112 -0||| -69: -2120 -0||0 -70: -2121 -0||- -71: -2122 -0|0| -72: -2200 -0|00 -73: -2201 -0|0- -74: -2202 -0|-| -75: -2210 -0|-0 -76: -2211 -0|-- -77: -2212 -00|| -78: -2220 -00|0 -79: -2221 -00|- -80: -2222 -000| -81: -10000 -0000
               balanced
    n   base 3  ternary

41: 1112 |---- 42: 1120 |---0 43: 1121 |---| 44: 1122 |--0- 45: 1200 |--00 46: 1201 |--0| 47: 1202 |--|- 48: 1210 |--|0 49: 1211 |--|| 50: 1212 |-0-- 51: 1220 |-0-0 52: 1221 |-0-| 53: 1222 |-00- 54: 2000 |-000 55: 2001 |-00| 56: 2002 |-0|- 57: 2010 |-0|0 58: 2011 |-0|| 59: 2012 |-|-- 60: 2020 |-|-0 61: 2021 |-|-| 62: 2022 |-|0- 63: 2100 |-|00 64: 2101 |-|0| 65: 2102 |-||- 66: 2110 |-||0 67: 2111 |-||| 68: 2112 |0--- 69: 2120 |0--0 70: 2121 |0--| 71: 2122 |0-0- 72: 2200 |0-00 73: 2201 |0-0| 74: 2202 |0-|- 75: 2210 |0-|0 76: 2211 |0-|| 77: 2212 |00-- 78: 2220 |00-0 79: 2221 |00-| 80: 2222 |000- 81: 10000 |0000

See table (values from 1 to 364).


Revision date: 2011-12-09. (Please use ISO 8601, the International Standard Date and Time Notation.)