[C] Telephone Number Word Generator

Started by
20 comments, last by Koolchamp 15 years, 5 months ago
Algorithmically, it looks like:

For each possible output Oo for the other digits:  For each possible letter L for the first digit:    Use recursion to output (L, followed by Oo, followed by a newline).


However, L has to get inserted "in between" the Oo outputs, which makes things tricky. Also, it isn't clear what to do when we hit the base case of the recursion.

The solution is to pass the "result so far" as a parameter for the recursion. It looks like:

We are given Op, which is some possible output for the previous digits.If there are no more digits, output Op and a newline, and return (we are done).For each possible output Oo for the next digits:  For each possible letter L for the current digit:    Call the function recursively, using Op + L for the new value of Op.


This way, we build up the output string as we "dive into" the recursion, and each time we "hit bottom", we get another of the possible options.

It also works to reverse the order of the two loops - that way gives a different sorting order. As an exercise, reason out what the order will be with each approach, and then test it.

Keep in mind that calculating "Op + V" is tricky in C. You can't just add strings like that; you'll have to append the V character manually.

If recursion scares you, you might be able to get your head around it by imagining that you are writing several functions: one that handles seven letters and zero digits by outputting the letters; one that handles six letters and one digit by generating each letter that's possible for the digit, and then calling the seven-letter-and-zero-digit function with each; one that handles five letters and two digits by generating each letter that's possible for the first digit, and then calling the six-letter-and-one-digit function with each of those options, etc. And then you can observe that, with a little bit of generalization work, these functions are actually all the same. And there's really nothing different about a function calling itself, versus calling another function; it's still calling a function, which works in the same way, with the new call getting its own copy of parameters and local variables. You just have to make sure that you don't get stuck in a loop, by defining a base case.
Advertisement
*bangs head on desk*

This should be pretty easy for someone who's passed intermediate algebra should it? I just don't understand why I can't wrapped my head around how to solve this. Zahlman dang near gave me the answer and I still don't get it. Maybe I should start with something more basic. Try to do something that's similar but on a smaller scale.

It's a good thing this isn't do for another two weeks otherwise I'd be in big trouble.
Cheers,Ken(Koolchamp)_____________________________"Choose a job you love, and you will never have to work a day in your life." - Confucius"If you don't have a game industry job and you want to go to E3, do what everyone else does. Launch a game review website and call yourself press." - Mike McShaffry(This is true for me) “…..I'm a geek and jocks are my natural enemy.” – Li C. Kuo
Quote:Original post by Koolchamp
I've never actually counted (in coding form) in anything but base 10.

So let's count on paper:

0 1 2 3 4 5... Hm, what's happening here? How do we know after 0 cames 1, after 1 comes 2, after 2 comes 3 and so on? These are just the digits of our decimal system, written down in ascending order. We just KNOW that, there's no further explanation. Let's go on:

5 6 7 8 9 10... Wait a minute, what just happened? Apparently, 9 is the last digit that we know, so we can't just use the next digit - there simply is none. The digit reverts back to 0, and we introduce a second digit to the left that now has the value 1. Let's go on:

10 11 12 13 14 15 16 17 18 19 20... Aha! The first digit reverted back to 0 once again, and the second digit changed from 1 to 2. This happens again ten steps later:

20 21 22 23 24 25 26 27 28 29 30... See? Now what happens if the second digit also reaches 9? Let's analyse:

90 91 92 93 94 95 96 97 98 99 100... Do you see a pattern? Apparently, every time the leftmost digit overflows, we introduce a new digit with the value 1. Thus we get arbitrarily large numbers.

Now, if you handle fixed width numbers, we start with all digits at 0. Leading zeroes don't change the value of the number, 007 is the same as 7 (except when James Bond is involved). This simplifies matters, because we don't have to introduce new digits. For example, three digit numbers:

000 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030... 090 091 092 093 094 095 096 097 098 099 100...

What happens if we reach the last number?
990 991 992 993 994 995 996 997 998 999 000... Aha, we're back to the start!

So how do we get from 198 to 199 for example? We start of the rightmost digit. 8 is not the highest possible digit, so we just set it to the next digit 9. Done, 199 is the next number. That was simple!

Now what's the next number? We can't just increase the rightmost digit, because it is already at the maximum. So we set it back to 0 and look at the next digit. Again, it is already at the maximum, so we set that to 0 and look at the next digit. 1 is increased to 2, and we get 200. You can implement this as recursion or iteration.

Now just replace the width three with whatever you need, I think it was seven, and replace the digits 0123456789 with your letters, I think it was ABC.
Quote:Original post by DevFred
replace the digits 0123456789 with your letters, I think it was ABC.

Oh, I just realized you don't have a fixed set of digits like ABC for the entire number, but every position has its own set of digits. But I'm sure you will figure that out :)

Here's a little program I wrote that counts every possible combination of ABC in a seven digit number:

#include <iostream>#include <string>#include <vector>template<unsigned width> class Number{    std::string available_digits;    std::vector<int> current_value;public:    Number(std::string available_digits) :        available_digits(available_digits),        current_value(width)    {}    bool count()    {        const int bound = available_digits.size();        for (int i = 0; i < width; ++i)            // increase digit and check for overflow            if (++current_value < bound)                return true; // no overflow -> get out of here            else                current_value = 0; // overflow -> reset and continue        // we have set every digit back to 0        // and indicate that by returning false        return false;    }    friend std::ostream& operator<<(std::ostream& os, const Number& number)    {        // output the digits of the value        // from highest position to lowest position        for (int i = width - 1; i >= 0; --i)            os << number.available_digits[number.current_value];        return os;    }};int main(){    Number<7> n("ABC");    int perm = 0;    do        std::cout << n << std::endl;    while (++perm, n.count());    std::cout << perm << " permutations\n";}
Here's how I'd go about solving it.

A recursive solution is easiest, but as recursion in C is generally unpretty I'd write it in erlang:

-module(tel).-compile(export_all).-define(KEYS, 	dict:from_list([		 {2, [a, b, c]}, {3, [d, e, f]}, {4, [g, h, i]}, {5, [j, k, l]}, 	 {6, [m, n, o]}, {7, [p, r, s]}, {8, [t, u, v]}, {9, [w, x, y]} ])).l_to_upper(L) ->	[string:to_upper(atom_to_list(Elt)) || Elt <- L].cartesian([]) ->	[[]];cartesian([H | R]) ->	[[Elt | C] || Elt <- H, C <- cartesian(R)].num_to_letters(Num) ->	Letters = [dict:fetch(N, ?KEYS) || N <- Num],	[l_to_upper(L) || L <- cartesian(Letters)].


Hey, it works!

14> c(tel).                                     {ok,tel}15> tel:num_to_letters([2,3,4,5,5,6,7]).        [["A","D","G","J","J","M","P"], ["A","D","G","J","J","M","R"], ["A","D","G","J","J","M","S"],...16> length(tel:num_to_letters([2,3,4,5,5,6,7])).2187


Then I'd write an erlang-to-C compiler, and it's done.

Writing the erlang-to-C compiler is left as an exercise for the reader.
I think I understand the pattern of how to change it. For a number 3572468, it would go like this yes:

D J P A G M T
D J P A G M U
D J P A G M V
D J P A G N T <-- M changes to N, and we loop back to the beginning of 8's set.
D J P A G N U
D J P A G N V
D J P A G O T <-- N changes to O, and we loop back to the beginning of 8s set.
etc...

Is that the patter of change I should try to achieve? If it is, it would seem like I would need a counter for each phone number position along with if statements to check for looping back the counters when one counter reaches 3. If that's the case I guess that's where recursion might come into play, but unfortunetly I still can't seem to visualize it, even with the examples that have been provided thus far (which I appreciate by the way).
Cheers,Ken(Koolchamp)_____________________________"Choose a job you love, and you will never have to work a day in your life." - Confucius"If you don't have a game industry job and you want to go to E3, do what everyone else does. Launch a game review website and call yourself press." - Mike McShaffry(This is true for me) “…..I'm a geek and jocks are my natural enemy.” – Li C. Kuo
Quote:Original post by Koolchamp
D J P A G M T
D J P A G M U
D J P A G M V
D J P A G N T <-- M changes to N, and we loop back to the beginning of 8's set.
D J P A G N U
D J P A G N V
D J P A G O T <-- N changes to O, and we loop back to the beginning of 8s set.
etc...

Is that the patter of change I should try to achieve?

Looks good.
Quote:Original post by Koolchamp
If it is, it would seem like I would need a counter for each phone number position along with if statements to check for looping back the counters when one counter reaches 3. If that's the case I guess that's where recursion might come into play, but unfortunetly I still can't seem to visualize it, even with the examples that have been provided thus far (which I appreciate by the way).


Each recursive call provides one "counter". That counter just counts 3 times; it gets automatically reset when it returns to the parent recursive call, and then the parent recursive call updates *its* counter, and re-calls recursively again.
Quote:Original post by Zahlman
and then the parent [...] re-calls recursively again.

Unless the parent is the last counter, then we must also stop the recursion (that only happens if we try to increment the highest number and wrap around to 0000000).
I'm a software engineer, but this took me longer to implement than I expected. My solution is still kinda ugly, but it appears to work.
There are a lot of ways to solve this problem. It would be interesting to compare implementations when this assignment is complete.

Shawn

When using the Windows calculator program, always remember to clear any values from memory before exiting to prevent burn-in.

This topic is closed to new replies.

Advertisement