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:
- Create an array containing one string for each song in the songbook
- Sort the array by the number of occurrences of the digit in question
- 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:
-
function CountOccurrences(S: string; AInt: Integer): Integer;
-
var
-
I: Integer;
-
begin
-
Result := 0;
-
for I := 1 to Length(S) do
-
if S[I] = Chr(AInt + 48) then
-
Inc(Result);
-
end;
-
-
function GetCount(SongsInBook, SongsInService, Digit: Integer): Integer;
-
var
-
I, J: Integer;
-
Numbers: array of string;
-
TempStr: string;
-
begin
-
//Make sure the numbers are as expected
-
Assert(SongsInService <SongsInBook);
-
Assert((Digit <10) and (Digit> -1));
-
//First, fill an array with all the numbers in the book
-
SetLength(Numbers, SongsInBook);
-
for I := 0 to SongsInBook - 1 do
-
begin
-
Numbers[I] := IntToStr(I + 1);
-
end;
-
//Sort the array by the number of occurences of the digit in question. We'll
-
//just use a simple sort.
-
for I := 0 to SongsInBook - 1 do
-
begin
-
for J := I + 1 to SongsInBook - 1 do
-
begin
-
if CountOccurrences(Numbers[J], Digit)>=
-
CountOccurrences(Numbers[I], Digit) then
-
begin
-
TempStr := Numbers[I];
-
Numbers[I] := Numbers[J];
-
Numbers[J] := TempStr;
-
end;
-
end;
-
end;
-
//Finally, get the number of digits in the top SongsInService "worst" songs
-
TempStr := '';
-
for I := 0 to SongsInService - 1 do
-
TempStr := TempStr + Numbers[I];
-
Result := CountOccurrences(TempStr, Digit);
-
end;
-
-
function GetDigitCounts(SongsInBook, SongsInService: Integer): THymnBoard;
-
{var
-
I: Integer;}
-
begin
-
Result.N0 := GetCount(SongsInBook, SongsInService, 0);
-
Result.N1 := GetCount(SongsInBook, SongsInService, 1);
-
Result.N2 := GetCount(SongsInBook, SongsInService, 2);
-
Result.N3 := GetCount(SongsInBook, SongsInService, 3);
-
Result.N4 := GetCount(SongsInBook, SongsInService, 4);
-
Result.N5 := GetCount(SongsInBook, SongsInService, 5);
-
Result.N6 := GetCount(SongsInBook, SongsInService, 6);
-
Result.N7 := GetCount(SongsInBook, SongsInService, 7);
-
Result.N8 := GetCount(SongsInBook, SongsInService, 8);
-
Result.N9 := GetCount(SongsInBook, SongsInService, 9);
-
-
//The following also works, and is much shorter, but is less readable:
-
{ for I := 0 to 9 do
-
Integer(Pointer(Integer(@Result) + I * sizeof(Integer))^) :=
-
GetCount(SongsInBook, SongsInService, I);}
-
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.