Making Conway "look and say" numbers

I was asked to write a small C ++ 11 program that would generate so called Conway numbers, or "watch and talk". Essentially, given the nth term, for example. 11112, the next is just the pronunciation of the first term; in our case 4112, because there were 4 1 and only one 2. Another example: from 13 to 1113.

Below is the complete source code of the program for completeness (omit stdafx.h include if not compile with MS Visual Studio):

#include "stdafx.h"
#include <iostream>
#include <algorithm>
#include <numeric>
#include <string>
#include <vector>

using namespace std;

void print_usage(){

    cout << endl << "Conway Number Generator";
    cout << endl << "Arguments: initializer followed by nth term to be printed" << endl;
}

string say(string s){

    // Returns string said phonetically
    // e.g. '111' becomes '31'

    unsigned int sz = s.size();

    if (sz == 1){
        s.insert(0, "1");
    }
    else{
        s.insert(0, to_string(sz));
        s.erase(2, sz-1);
    }
    return s;
}

string get_next(string x){

    // Returns next term in Conway sequence

    unsigned prev_point = 0;
    vector<string> grp;
    string tmp;
    tmp.resize(x.size());

    // Organize sequence in group of identical consecutive characters

    for (unsigned int i = 0; i < x.size(); ++i){

        if (i != x.size() - 1){

            if (x.at(i) != x.at(i + 1)){
                tmp.assign(x, prev_point, i - prev_point);
                grp.push_back(tmp);
                prev_point = i + 1;
                tmp.clear();
            }
        }
        else if (i != 0){

            if (x.at(i) != x.at(i - 1)){
                tmp = x.at(i);
                grp.push_back(tmp);
                tmp.clear();
            }
        }
    }

    // Phonetically say each group

    // Could use a lambda: transform(begin(grp), end(grp), begin(said_grp)[=](string s){return say(s);});
    // if I create a new vector<string> said_grp to copy in
    // That didn't help the runtime error

    for (auto& i : grp)
        i = say(i);

    // Join each vector<string> entry into a single string

    string next_seq;
    next_seq = accumulate(begin(grp), end(grp), string(""));
    return next_seq;
}

string conway(string init, int n){

    // Print out nth Conway term
    // starting sequence at init

    for (int i = 1; i <= n; ++i)
        init = get_next(init);
    return init;
}

int main(int argc, const char* argv[]){

    if (argc < 3){
        print_usage();
        return 0;
    }
    cout << endl << "Conway number: " << conway(argv[1], stoi(argv[2])) << endl;
    return 0;
}

      

General approach:

  • Accept two arguments: the first member of the Conway sequence and an integer n

    select the calculation of the nth term of this sequence.
  • The function conway(...)

    loops get_next()

    over the source string n

    times.
  • The say()

    ' function speaks numbers.
  • get_next()

    functions by breaking the whole number into consecutive identical numbers; each is then converted to say()

    .

The problem I am facing is the error std::out_of_range

, from the return statement of my function say()

. However, I can say()

only remember testing with different inputs, and this never threw an exception. I must be using it wrong somehow. As I noted in the code comments, I tried using STL conversion instead of looping for

with lambda before, same error.


Note: For those interested in Conway's sequence, see Conway's original article. They allow for some interesting properties, and they seem to generate a constant known as Conway's constant, which has been shown to be algebraic.

+3


source to share


2 answers


As I mentioned in the comments, this is the perfect question to solve with a debugger. 80+ (very smart) people have looked at your code and no one has been able to completely solve it just by looking (although @hlt got the most of it). After a bit of debugging, I think I caught the rest. Let's take a look at the parts.

As with hlt, you need auto&

in a range based loop in get_next()

, otherwise you never change grp

and you keep returning your original number. See this question for more information.

Also, thanks to hlt, you had some missing logic regarding zero line length inputs. However, get_next()

there is another missing element in your function . Take the entrance, for example ./conway 111 2

. The number 111

will never actually be put into grp

, because all characters are the same, so if (x.at(i) != x.at(i + 1))

neither if (x.at(i) != x.at(i - 1))

would nor would ever evaluate to true; as such, you will get an empty string as the result. The missing part is shown below.



for (unsigned int i = 0; i < x.size(); ++i){

    if (i != x.size() - 1){

        if (x.at(i) != x.at(i + 1)){
            tmp.assign(x, prev_point, i+1 - prev_point);
            grp.push_back(tmp);
            prev_point = i + 1;
            tmp.clear();
        }
    }
    else if (i != 0){

        if (x.at(i) != x.at(i - 1)){
            tmp = x.at(i);
            grp.push_back(tmp);
            tmp.clear();
        }
        else
        {
            tmp.assign(x, prev_point, i+1 - prev_point);
            grp.push_back(tmp);
            tmp.clear();
        }
    }

      

The last issue I mentioned in my comment above and is included in the code here. To assign a substring of the correct length tmp

, you need i+1-prev_point

, not i-prev_point

.

I said to start, I think I caught the rest of the errors, but that may not be correct. I would recommend writing multiple tests with a lot of numbers with different characteristics (e.g. 1, 12121212, 0000, 1222, 2221, etc.) and their expected results. If any of these are giving unexpected results, then it's time to return to the debugger.

+2


source


I looked at your source code and noticed that your get_next () function could be simplified. I replaced it with the following version:

string get_next(const string & inStr) {
    string result;
    int len = inStr.size();
    int count = 0;
    // Loop over non-duplicates
    for (int i = 0; i < len; i += count) {
        // Calculate total number of duplicates for inStr[i]:
        for (count = 0; i+count < len; count++) {
            if (inStr[i+count] != inStr[i]) {
                break;
            }
        }
        // Append count + non-duplicate (count and say)
        result += to_string(count) + inStr[i];
    }
    return result;
}

      

When I run new code with "1" as the starting line and 5 as n, I get the following output:



Conveyor number: 312211

which is true: 1 → 11 → 21 → 1211 → 111221 → 312211

No debugging required. Hope this helps.

0


source







All Articles