What is a natural way to enumerate the symmetries of a cube? I want to define a labeling, by small natural numbers, of the 48 symmetries of a cube — affine transformations which do not change the volume it occupies. What is a straightforward/​​natural/​​aesthetically pleasing way to do this?
Here are some facts that might constrain the problem better:


*

*It would be nice if the identity is numbered 0.

*It would be nice if “ordinary” ones (e.g. rotations about one face-centered axis) had smaller numbers.

*It would be nice if the digits of the numbering had significance in base 2 (an example would be if $i \bmod 2$ indicated whether transformation $i$ was a reflection), or otherwise gave meaning to arithmetic operations on them.

*In the relevant coordinate system, the cube is axis-aligned and has opposite corners $(0,0,0)$ and $(d,d,d)$.

*This is going to be implemented in a computer program, and the symmetries are eventually going to be used in the form of a precomputed table of points and basis vectors, so the best form would be a function of the symmetry number, a point to transform, and $d$.
(In case it's interesting: I'm working with axis-aligned voxels, i.e. $\mathbb{Z}^3$ rather than $\mathbb{R}^3$. Non-right angles are therefore completely irrelevant to this part of my work.)

*It would be nice if each symmetry has only one number, but it is not absolutely necessary.
I am not especially familiar with group theory; even less so are most people who are going to be using this numbering.
[Please improve the tags on this question if you see fit; I've just imitated similar-looking questions.]
 A: I'll describe a natural system (called a chief series for the algebraists in the eaves) to enumerate the symmetries of the cube (and coincidentally, at the same time symmetries of other related objects).  The very end has code to apply the symmetries.
I require two concessions: a few values are unused (bits 2,3 cannot both be 1), and we actually apply the transformation rather than represent it as a big matrix.  The first part is no big deal since you just loop i over {0,1}, then j over {0,1}, then k over {0,1,...,11} and use 32*i + 16*j + k.  The second part should also be no big deal, as writing down the matrix is approximately the same as applying the transformation to three of the corners of the cube.
Now the description of the numbering:
The numbers 0 through 3 are used for the Klein four-group which consists of the 180 degree rotations.  I suggest:


*

*0 is the identity matrix

*1 is the XY plane 180 degree rotation, (x,y,z) → (d−x,d−y,z)

*2 is the YZ plane 180 degree rotation, (x,y,z) → (x,d−y,d−z)

*3 is the XZ plane 180 degree rotation, (x,y,z) → (d−x,y,d−z)


Composition of these symmetries is simply XOR.
The numbers 4 through 11 are used for the 120 degree rotations of the tetrahedron sitting inside the cube.  In other words, take a "main diagonal" of the cube, and rotate around it.  It has a 3 fold symmetry, 120 degree rotations.   Taking the diagonal from (0,0,0) to (d,d,d) one gets a particularly easy transformation: (x,y,z) → (y,z,x).  At any rate, 4 through 7 are do that 120 degree rotation, and then do the 180 degree rotation 0 through 3.  8 through 11 are do that 120 rotation twice, so (x,y,z) → (z,x,y), and then the 180 degree rotation 0 through 3.  Composition is a hassle (add the high nibble mod 3, but take one of the high nibbles and use it swirl the low nibble).
The numbers 16 through 27 are used for the rotational symmetries of the cube that switch the interior tetrahedrons.  This is also very easy to describe as a transformation: if bit 8 is set, then apply the rotation you get by holding opposite vertical edges of the cube and rotating them 180: (x,y,z) → (y,x,d−z).
The numbers 32 through 43 and 48 through 59 are used for the reflectional symmetries of the cube (the first batch being symmetries of the two tetrahedra, and the second batch swapping two tetrahedra).  I like to use the coordinate swap of the first two coordinates for this: (x,y,z) → (y,x,z).
Here is actual code in good ole javascript to do it:
applySymmetry = function( which, d, x, y, z ) {
   var t;
   // Peel off the "are we a reflection?" bit
   if( which & 32 ) { t=x; x=y; y=t; }
   // Peel off the "do we swap the tetrahedrons?" bit
   if( which & 16 ) { t=x; x=y; y=t; z=d-z; }
   // Now we are in tetrahedral group, peel off the "120-ness"
   switch( (which & (4+8) ) >> 2 ) {
     case 0: break;
     case 1: t=x; x=y; y=z; z=t; break;
     case 2: t=z; z=y; y=x; x=t; break;
     case 3: alert('Not cool dude'); break;
   }
   // Now we are in the Klein four group, peel off the "180-ness"
   switch( which & (1+2) ) {
     case 0: break;
     case 1: x=d-x; y=d-y; break;
     case 2: y=d-y; z=d-z; break;
     case 3: z=d-z; x=d-x; break;
   }
   return [x,y,z];
}

applyAllSymmetries = function( d, x, y, z ) { // return the orbit
  var i, j, k, ret;
  ret = [];
  for( i = 0 ; i < 2 ; i++ ) {
    for( j = 0 ; j < 2 ; j++ ) {
      for( k = 0 ; k < 12 ; k++ ) {
         ret.push( applySymmetry( 32*i+16*j+k, d, x, y, z ) );
      }
    }
  }
  return ret;
}

This code is meant to exhibit the chief series.  You could optimize the applySymmetry function by combining the handling of bits 16 and 32, and you could optimize applyAllSymmetries by using a "walk on the Cayley graph" (instead of using coset representatives like I am).
Also you could swap the order of bits 4 and 5 if you wanted to focus on the "tetrahedron versus cube" instead of "rotation versus reflection".
A: There is no sensible "labeling, by small natural numbers" (apart from Jack's, which is quite sensible!)
Let $(e,f)$ be an oriented air of adjacent vertices. Every rotation fixing the cube is completely determined by the image of the ordered pair $(e,f)$, and every affine symmetric is determined by the image of $(e,f)$ and its determinant, which is either $1$ or $-1$.
This provides a very natural way of labelling the elements of the group.
