HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Computing the Ackermann Function

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
ackermannthefunctioncomputing

Problem

How can I improve this code? It computes the Ackermann function as long as m<4 and n<13.

#include 
#include 

// Ackermann function calculations
unsigned int ackermann(unsigned int m, unsigned int n){
    if(m == 0)
        return n+1;
    if(n == 0)
        return ackermann(m-1,1);
    return ackermann(m-1,ackermann(m,n-1));
}

// Check for non-integer input
bool inputCheck(){
    if(!std::cin.fail())
        return false;

    std::cout  3){
        std::cout  3)!"  12){
        std::cout  12)!" > m;
        valM = check(m, 'm');
    } while(valM);

    do{
        std::cout > n;
        valN = check(n, 'n');
    } while(valN);

    std::cout << "\nM = " << m << "\nN = " << n
            << "\n\nCALCULATING..." << std::endl;

    std::cout << "A(" << m << ',' << n << ") = "
            << ackermann(m,n) << std::endl;

    return 0;
}


A majority of the code is for detecting errors (such as invalid or negative input) and for preventing overflow errors (such as Ackermann(4,2)). A minority of the code actually calculates the Ackermann function.

Solution

bool inputCheck(){
    if(!std::cin.fail())
        return false;

    std::cout << "Invalid input!" << std::endl;
    return true;
}


Carefully name your functions with a meaningful name. inputCheck doesn't really tell anyone what to expect as a result.

Prefer to distinguish language constructs with spaces.

You can use the contextual boolean conversion operator to check the if the stream has failed rather than explicitly calling !std::cin.fail().

Prefer to avoid std::endl. Be aware of what the manipulator std::endl actually does. Stream '\n' as it explicitly states your intent, correctly outputs the end-of-line character, and is shorter to type.

bool isInvalidInput() {
    if (std::cin) {
        return false;
    }
    std::cout << "Invalid input!\n";
    return true;
}


bool numCheck(int i, char name){
    if(i  3){ /* ... */ }
    if(name == 'n' && i > 12){ /* ... */ }
    return false;
}


Specify const for immutable variables. const self-documents that a variable should not change values in the current scope and any accidental modification to the value is detected at compile time rather than run time.

Prefer enumerations to represent sets of related named constants.

Avoid magic constants as they are difficult to understand and may be overlooked. Prefer symbolic constants to give values contextual meaning.

bool check(int x, char y){
    bool result = inputCheck() || numCheck(x, y);
    std::cin.clear();
    std::cin.ignore();
    return result;
}


Do you really want to ignore the remaining buffer on success?

unsigned int ackermann(unsigned int m, unsigned int n){
    if(m == 0)
        return n+1;
    if(n == 0)
        return ackermann(m-1,1);
    return ackermann(m-1,ackermann(m,n-1));
}


The Ackermann–Péter function should be tail-call optimized by any decent compiler, so you won't find much improvement with the recursive approach. If you really care for performance, calculate Ackerman values in constant time using the formula's for \$A(m,n)\$.

$$
A(0,n) = n + 1\\
A(1,n) = n + 2\\
A(2,n) = 2n + 3\\
A(3,n) = 2^{(n+3)} - 3\\
A(4,0) = 13\\
A(4,1) = A(5,0) = 65533
$$

Contractually enforce your preconditions by testing them in the function that requires them.

namespace impl {
    unsigned Ackermann(unsigned m, unsigned n) {
        // calculate A(m,n)
    }
    void check_overflow_bounds(unsigned m, unsigned n) {
        if (m > 3 || n > 12) {
            throw std::out_of_bounds("");
        }
    }
}
unsigned Ackermann(unsigned m, unsigned n) {
    impl::check_overflow_bounds(m, n);
    return impl::Ackermann(m, n);
}


If your return type is an unsigned int, will Ackermann values overflow if \$m < 4\$ and \$n = 13\$? Are there Ackermann values that don't overflow when \$m = 4\$ or \$m = 5\$? Consider what actually is computable and throw an overflow exception for values that are not computable.

Code Snippets

bool inputCheck(){
    if(!std::cin.fail())
        return false;

    std::cout << "Invalid input!" << std::endl;
    return true;
}
bool isInvalidInput() {
    if (std::cin) {
        return false;
    }
    std::cout << "Invalid input!\n";
    return true;
}
bool numCheck(int i, char name){
    if(i < 0){ /* ... */ }
    if(name == 'm' && i > 3){ /* ... */ }
    if(name == 'n' && i > 12){ /* ... */ }
    return false;
}
bool check(int x, char y){
    bool result = inputCheck() || numCheck(x, y);
    std::cin.clear();
    std::cin.ignore();
    return result;
}
unsigned int ackermann(unsigned int m, unsigned int n){
    if(m == 0)
        return n+1;
    if(n == 0)
        return ackermann(m-1,1);
    return ackermann(m-1,ackermann(m,n-1));
}

Context

StackExchange Code Review Q#142304, answer score: 3

Revisions (0)

No revisions yet.