Skip to content

Answer to “Sing a Song”

This puzzle came about a couple weeks ago when I was in a church. I looked at the hymn board and discovered that there were so many "1"s on the board that they had run out, and someone had turned a "7" upside-down and covered the horizontal part with masking tape. I thought, "there must be a simple way to know how many of each digit card to include." I came up with one method, but thought it would be interesting to see if anyone else could come up with something better.

Here's the method I used:

  1. Create an array containing one string for each song in the songbook
  2. Sort the array by the number of occurrences of the digit in question
  3. Get the number of occurrences of the digit in the first X strings in the array, where X is the number of songs in the service.

Here's my code:

  1. function CountOccurrences(S: string; AInt: Integer): Integer;
  2. var
  3.   I: Integer;
  4. begin
  5.   Result := 0;
  6.   for I := 1 to Length(S) do
  7.     if S[I] = Chr(AInt + 48) then
  8.       Inc(Result);
  9. end;
  10.  
  11. function GetCount(SongsInBook, SongsInService, Digit: Integer): Integer;
  12. var
  13.   I, J: Integer;
  14.   Numbers: array of string;
  15.   TempStr: string;
  16. begin
  17.   //Make sure the numbers are as expected
  18.   Assert(SongsInService <SongsInBook);
  19.   Assert((Digit <10) and (Digit> -1));
  20.   //First, fill an array with all the numbers in the book
  21.   SetLength(Numbers, SongsInBook);
  22.   for I := 0 to SongsInBook - 1 do
  23.   begin
  24.     Numbers[I] := IntToStr(I + 1);
  25.   end;
  26.   //Sort the array by the number of occurences of the digit in question. We'll
  27.   //just use a simple sort.
  28.   for I := 0 to SongsInBook - 1 do
  29.   begin
  30.     for J := I + 1 to SongsInBook - 1 do
  31.     begin
  32.       if CountOccurrences(Numbers[J], Digit)>=
  33.          CountOccurrences(Numbers[I], Digit) then
  34.       begin
  35.         TempStr := Numbers[I];
  36.         Numbers[I] := Numbers[J];
  37.         Numbers[J] := TempStr;
  38.       end;
  39.     end;
  40.   end;
  41.   //Finally, get the number of digits in the top SongsInService "worst" songs
  42.   TempStr := '';
  43.   for I := 0 to SongsInService - 1 do
  44.     TempStr := TempStr + Numbers[I];
  45.   Result := CountOccurrences(TempStr, Digit);
  46. end;
  47.  
  48. function GetDigitCounts(SongsInBook, SongsInService: Integer): THymnBoard;
  49. {var
  50.   I: Integer;}
  51. begin
  52.   Result.N0 := GetCount(SongsInBook, SongsInService, 0);
  53.   Result.N1 := GetCount(SongsInBook, SongsInService, 1);
  54.   Result.N2 := GetCount(SongsInBook, SongsInService, 2);
  55.   Result.N3 := GetCount(SongsInBook, SongsInService, 3);
  56.   Result.N4 := GetCount(SongsInBook, SongsInService, 4);
  57.   Result.N5 := GetCount(SongsInBook, SongsInService, 5);
  58.   Result.N6 := GetCount(SongsInBook, SongsInService, 6);
  59.   Result.N7 := GetCount(SongsInBook, SongsInService, 7);
  60.   Result.N8 := GetCount(SongsInBook, SongsInService, 8);
  61.   Result.N9 := GetCount(SongsInBook, SongsInService, 9);
  62.  
  63. //The following also works, and is much shorter, but is less readable:
  64. {  for I := 0 to 9 do
  65.     Integer(Pointer(Integer(@Result) + I * sizeof(Integer))^) :=
  66.       GetCount(SongsInBook, SongsInService, I);}
  67. end;

There are a couple of optimizations that could be done here that I didn't do for the sake of readability. One is that the array doesn't necessarily need to be regenerated each time, but really only needs to be re-sorted for each digit. The array could be generated only once.

Also, a better sorting algorithm than simple sort could obviously be used, but the sorting isn't the point here, so I decided to keep the code as simple as possible.

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*