Computing the Ackermann Function The Next CEO of Stack OverflowComputing the distribution of a Jonckheere-Terpstra statisticGeneric absolute value functionHaskell Ackermann functionComputing the n-th primeComputing the de Bruijn sequenceIterative implementation of the Ackermann functionComputing the Mean Average PrecisionComputing the improper integral of several functionsComputing the gradient of a functionComputing function time in C++
Math-accent symbol over parentheses enclosing accented symbol (amsmath)
Why do airplanes bank sharply to the right after air-to-air refueling?
Why don't programming languages automatically manage the synchronous/asynchronous problem?
How to avoid supervisors with prejudiced views?
Is it possible to replace duplicates of a character with one character using tr
Prepend last line of stdin to entire stdin
Is it okay to majorly distort historical facts while writing a fiction story?
Why is the US ranked as #45 in Press Freedom ratings, despite its extremely permissive free speech laws?
Recycling old answers
If Nick Fury and Coulson already knew about aliens (Kree and Skrull) why did they wait until Thor's appearance to start making weapons?
RigExpert AA-35 - Interpreting The Information
Domestic-to-international connection at Orlando (MCO)
Newlines in BSD sed vs gsed
Is it professional to write unrelated content in an almost-empty email?
WOW air has ceased operation, can I get my tickets refunded?
How to sed chunks text from a stream of files from find
Why doesn't UK go for the same deal Japan has with EU to resolve Brexit?
Is there a way to save my career from absolute disaster?
Why, when going from special to general relativity, do we just replace partial derivatives with covariant derivatives?
Need help understanding a power circuit (caps and diodes)
Rotate a column
Writing differences on a blackboard
How to get from Geneva Airport to Metabief?
The exact meaning of 'Mom made me a sandwich'
Computing the Ackermann Function
The Next CEO of Stack OverflowComputing the distribution of a Jonckheere-Terpstra statisticGeneric absolute value functionHaskell Ackermann functionComputing the n-th primeComputing the de Bruijn sequenceIterative implementation of the Ackermann functionComputing the Mean Average PrecisionComputing the improper integral of several functionsComputing the gradient of a functionComputing function time in C++
$begingroup$
How can I improve this code? It computes the Ackermann function as long as m<4
and n<13
.
#include <iostream>
#include <limits>
// 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 << "Invalid input!" << std::endl;
return true;
// Check for negative or overflow errors
bool numCheck(int i, char name)
if(i < 0)
std::cout << "Negative error!" << std::endl;
return true;
if(name == 'm' && i > 3)
std::cout << "Overflow error (m > 3)!" << std::endl;
return true;
if(name == 'n' && i > 12)
std::cout << "Overflow error (n > 12)!" << std::endl;
return true;
return false;
// Run input and num checks
bool check(int x, char y)
int main()
int m, n;
bool valM, valN;
do
std::cout << "m = ";
std::cin >> m;
valM = check(m, 'm');
while(valM);
do
std::cout << "n = ";
std::cin >> n;
valN = check(n, 'n');
while(valN);
std::cout << "nM = " << m << "nN = " << n
<< "nnCALCULATING..." << 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.
c++ performance c++14
$endgroup$
add a comment |
$begingroup$
How can I improve this code? It computes the Ackermann function as long as m<4
and n<13
.
#include <iostream>
#include <limits>
// 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 << "Invalid input!" << std::endl;
return true;
// Check for negative or overflow errors
bool numCheck(int i, char name)
if(i < 0)
std::cout << "Negative error!" << std::endl;
return true;
if(name == 'm' && i > 3)
std::cout << "Overflow error (m > 3)!" << std::endl;
return true;
if(name == 'n' && i > 12)
std::cout << "Overflow error (n > 12)!" << std::endl;
return true;
return false;
// Run input and num checks
bool check(int x, char y)
int main()
int m, n;
bool valM, valN;
do
std::cout << "m = ";
std::cin >> m;
valM = check(m, 'm');
while(valM);
do
std::cout << "n = ";
std::cin >> n;
valN = check(n, 'n');
while(valN);
std::cout << "nM = " << m << "nN = " << n
<< "nnCALCULATING..." << 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.
c++ performance c++14
$endgroup$
add a comment |
$begingroup$
How can I improve this code? It computes the Ackermann function as long as m<4
and n<13
.
#include <iostream>
#include <limits>
// 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 << "Invalid input!" << std::endl;
return true;
// Check for negative or overflow errors
bool numCheck(int i, char name)
if(i < 0)
std::cout << "Negative error!" << std::endl;
return true;
if(name == 'm' && i > 3)
std::cout << "Overflow error (m > 3)!" << std::endl;
return true;
if(name == 'n' && i > 12)
std::cout << "Overflow error (n > 12)!" << std::endl;
return true;
return false;
// Run input and num checks
bool check(int x, char y)
int main()
int m, n;
bool valM, valN;
do
std::cout << "m = ";
std::cin >> m;
valM = check(m, 'm');
while(valM);
do
std::cout << "n = ";
std::cin >> n;
valN = check(n, 'n');
while(valN);
std::cout << "nM = " << m << "nN = " << n
<< "nnCALCULATING..." << 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.
c++ performance c++14
$endgroup$
How can I improve this code? It computes the Ackermann function as long as m<4
and n<13
.
#include <iostream>
#include <limits>
// 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 << "Invalid input!" << std::endl;
return true;
// Check for negative or overflow errors
bool numCheck(int i, char name)
if(i < 0)
std::cout << "Negative error!" << std::endl;
return true;
if(name == 'm' && i > 3)
std::cout << "Overflow error (m > 3)!" << std::endl;
return true;
if(name == 'n' && i > 12)
std::cout << "Overflow error (n > 12)!" << std::endl;
return true;
return false;
// Run input and num checks
bool check(int x, char y)
int main()
int m, n;
bool valM, valN;
do
std::cout << "m = ";
std::cin >> m;
valM = check(m, 'm');
while(valM);
do
std::cout << "n = ";
std::cin >> n;
valN = check(n, 'n');
while(valN);
std::cout << "nM = " << m << "nN = " << n
<< "nnCALCULATING..." << 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.
c++ performance c++14
c++ performance c++14
asked Sep 24 '16 at 3:26
esoteesote
2,89611038
2,89611038
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
$begingroup$
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 < 0) /* ... */
if(name == 'm' && 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) 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
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.
$endgroup$
$begingroup$
I have made most of the changes you recommended, and the code looks much nicer now. What do you mean by calculating the Ackermann values in constant time and validating preconditions?
$endgroup$
– esote
Sep 24 '16 at 15:21
$begingroup$
Since you only have (3*12)=36 possible input values, you can simply pre-calculate all the results and put them into a table. It can be a 3x12 table and you can simply look up the answer by indexing into it with m and n. Validating preconditions means ensuring that the inputs to your functions are in a valid range.
$endgroup$
– user1118321
Sep 24 '16 at 17:33
$begingroup$
Expanded on both your concerns in the edit.
$endgroup$
– Snowhawk
Sep 24 '16 at 18:11
add a comment |
$begingroup$
One way to improve is to use a database of lookup values to avoid repeated function calls. For example if your function calls ackermann(1,1)
many times, retrieving from a database will prevent your program from naïvely opening unnecessary stack frames.
(Note: I don’t know C++ so my answer is in JavaScript.)
/** Database: a two-dimensional array of numbers */
const db = []
/** Set a value to an entry in the database. */
db.set = function (m, n, value)
/** Retrieve a value from the database. */
db.get = function (m, n)
// if row is an array and cell is a number, return that; else throw error
if (this[m] instanceof Array && typeof this[m][n] === 'number')
return this[m][n]
throw new ReferenceError('No entry found.')
/** The Ackermann Function */
function ack(m, n) !Number.isFinite(m + n))
throw new RangeError('Only natural numbers allowed.')
/** The returned result (the answer) */
let returned;
try
// try to get a database entry, if it exists
returned = db.get(m, n)
catch (err)
// else, do Ackermann’s algorithm
if (m === 0)
returned = n + 1
else if (n === 0)
returned = ack(m - 1, 1)
else
returned = ack(m - 1, ack(m, n - 1))
// make a new database entry for the result
db.set(m, n, returned)
return returned
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f142304%2fcomputing-the-ackermann-function%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
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 < 0) /* ... */
if(name == 'm' && 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) 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
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.
$endgroup$
$begingroup$
I have made most of the changes you recommended, and the code looks much nicer now. What do you mean by calculating the Ackermann values in constant time and validating preconditions?
$endgroup$
– esote
Sep 24 '16 at 15:21
$begingroup$
Since you only have (3*12)=36 possible input values, you can simply pre-calculate all the results and put them into a table. It can be a 3x12 table and you can simply look up the answer by indexing into it with m and n. Validating preconditions means ensuring that the inputs to your functions are in a valid range.
$endgroup$
– user1118321
Sep 24 '16 at 17:33
$begingroup$
Expanded on both your concerns in the edit.
$endgroup$
– Snowhawk
Sep 24 '16 at 18:11
add a comment |
$begingroup$
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 < 0) /* ... */
if(name == 'm' && 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) 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
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.
$endgroup$
$begingroup$
I have made most of the changes you recommended, and the code looks much nicer now. What do you mean by calculating the Ackermann values in constant time and validating preconditions?
$endgroup$
– esote
Sep 24 '16 at 15:21
$begingroup$
Since you only have (3*12)=36 possible input values, you can simply pre-calculate all the results and put them into a table. It can be a 3x12 table and you can simply look up the answer by indexing into it with m and n. Validating preconditions means ensuring that the inputs to your functions are in a valid range.
$endgroup$
– user1118321
Sep 24 '16 at 17:33
$begingroup$
Expanded on both your concerns in the edit.
$endgroup$
– Snowhawk
Sep 24 '16 at 18:11
add a comment |
$begingroup$
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 < 0) /* ... */
if(name == 'm' && 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) 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
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.
$endgroup$
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 < 0) /* ... */
if(name == 'm' && 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) 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
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.
edited Sep 24 '16 at 18:10
answered Sep 24 '16 at 6:55
SnowhawkSnowhawk
5,55611229
5,55611229
$begingroup$
I have made most of the changes you recommended, and the code looks much nicer now. What do you mean by calculating the Ackermann values in constant time and validating preconditions?
$endgroup$
– esote
Sep 24 '16 at 15:21
$begingroup$
Since you only have (3*12)=36 possible input values, you can simply pre-calculate all the results and put them into a table. It can be a 3x12 table and you can simply look up the answer by indexing into it with m and n. Validating preconditions means ensuring that the inputs to your functions are in a valid range.
$endgroup$
– user1118321
Sep 24 '16 at 17:33
$begingroup$
Expanded on both your concerns in the edit.
$endgroup$
– Snowhawk
Sep 24 '16 at 18:11
add a comment |
$begingroup$
I have made most of the changes you recommended, and the code looks much nicer now. What do you mean by calculating the Ackermann values in constant time and validating preconditions?
$endgroup$
– esote
Sep 24 '16 at 15:21
$begingroup$
Since you only have (3*12)=36 possible input values, you can simply pre-calculate all the results and put them into a table. It can be a 3x12 table and you can simply look up the answer by indexing into it with m and n. Validating preconditions means ensuring that the inputs to your functions are in a valid range.
$endgroup$
– user1118321
Sep 24 '16 at 17:33
$begingroup$
Expanded on both your concerns in the edit.
$endgroup$
– Snowhawk
Sep 24 '16 at 18:11
$begingroup$
I have made most of the changes you recommended, and the code looks much nicer now. What do you mean by calculating the Ackermann values in constant time and validating preconditions?
$endgroup$
– esote
Sep 24 '16 at 15:21
$begingroup$
I have made most of the changes you recommended, and the code looks much nicer now. What do you mean by calculating the Ackermann values in constant time and validating preconditions?
$endgroup$
– esote
Sep 24 '16 at 15:21
$begingroup$
Since you only have (3*12)=36 possible input values, you can simply pre-calculate all the results and put them into a table. It can be a 3x12 table and you can simply look up the answer by indexing into it with m and n. Validating preconditions means ensuring that the inputs to your functions are in a valid range.
$endgroup$
– user1118321
Sep 24 '16 at 17:33
$begingroup$
Since you only have (3*12)=36 possible input values, you can simply pre-calculate all the results and put them into a table. It can be a 3x12 table and you can simply look up the answer by indexing into it with m and n. Validating preconditions means ensuring that the inputs to your functions are in a valid range.
$endgroup$
– user1118321
Sep 24 '16 at 17:33
$begingroup$
Expanded on both your concerns in the edit.
$endgroup$
– Snowhawk
Sep 24 '16 at 18:11
$begingroup$
Expanded on both your concerns in the edit.
$endgroup$
– Snowhawk
Sep 24 '16 at 18:11
add a comment |
$begingroup$
One way to improve is to use a database of lookup values to avoid repeated function calls. For example if your function calls ackermann(1,1)
many times, retrieving from a database will prevent your program from naïvely opening unnecessary stack frames.
(Note: I don’t know C++ so my answer is in JavaScript.)
/** Database: a two-dimensional array of numbers */
const db = []
/** Set a value to an entry in the database. */
db.set = function (m, n, value)
/** Retrieve a value from the database. */
db.get = function (m, n)
// if row is an array and cell is a number, return that; else throw error
if (this[m] instanceof Array && typeof this[m][n] === 'number')
return this[m][n]
throw new ReferenceError('No entry found.')
/** The Ackermann Function */
function ack(m, n) !Number.isFinite(m + n))
throw new RangeError('Only natural numbers allowed.')
/** The returned result (the answer) */
let returned;
try
// try to get a database entry, if it exists
returned = db.get(m, n)
catch (err)
// else, do Ackermann’s algorithm
if (m === 0)
returned = n + 1
else if (n === 0)
returned = ack(m - 1, 1)
else
returned = ack(m - 1, ack(m, n - 1))
// make a new database entry for the result
db.set(m, n, returned)
return returned
$endgroup$
add a comment |
$begingroup$
One way to improve is to use a database of lookup values to avoid repeated function calls. For example if your function calls ackermann(1,1)
many times, retrieving from a database will prevent your program from naïvely opening unnecessary stack frames.
(Note: I don’t know C++ so my answer is in JavaScript.)
/** Database: a two-dimensional array of numbers */
const db = []
/** Set a value to an entry in the database. */
db.set = function (m, n, value)
/** Retrieve a value from the database. */
db.get = function (m, n)
// if row is an array and cell is a number, return that; else throw error
if (this[m] instanceof Array && typeof this[m][n] === 'number')
return this[m][n]
throw new ReferenceError('No entry found.')
/** The Ackermann Function */
function ack(m, n) !Number.isFinite(m + n))
throw new RangeError('Only natural numbers allowed.')
/** The returned result (the answer) */
let returned;
try
// try to get a database entry, if it exists
returned = db.get(m, n)
catch (err)
// else, do Ackermann’s algorithm
if (m === 0)
returned = n + 1
else if (n === 0)
returned = ack(m - 1, 1)
else
returned = ack(m - 1, ack(m, n - 1))
// make a new database entry for the result
db.set(m, n, returned)
return returned
$endgroup$
add a comment |
$begingroup$
One way to improve is to use a database of lookup values to avoid repeated function calls. For example if your function calls ackermann(1,1)
many times, retrieving from a database will prevent your program from naïvely opening unnecessary stack frames.
(Note: I don’t know C++ so my answer is in JavaScript.)
/** Database: a two-dimensional array of numbers */
const db = []
/** Set a value to an entry in the database. */
db.set = function (m, n, value)
/** Retrieve a value from the database. */
db.get = function (m, n)
// if row is an array and cell is a number, return that; else throw error
if (this[m] instanceof Array && typeof this[m][n] === 'number')
return this[m][n]
throw new ReferenceError('No entry found.')
/** The Ackermann Function */
function ack(m, n) !Number.isFinite(m + n))
throw new RangeError('Only natural numbers allowed.')
/** The returned result (the answer) */
let returned;
try
// try to get a database entry, if it exists
returned = db.get(m, n)
catch (err)
// else, do Ackermann’s algorithm
if (m === 0)
returned = n + 1
else if (n === 0)
returned = ack(m - 1, 1)
else
returned = ack(m - 1, ack(m, n - 1))
// make a new database entry for the result
db.set(m, n, returned)
return returned
$endgroup$
One way to improve is to use a database of lookup values to avoid repeated function calls. For example if your function calls ackermann(1,1)
many times, retrieving from a database will prevent your program from naïvely opening unnecessary stack frames.
(Note: I don’t know C++ so my answer is in JavaScript.)
/** Database: a two-dimensional array of numbers */
const db = []
/** Set a value to an entry in the database. */
db.set = function (m, n, value)
/** Retrieve a value from the database. */
db.get = function (m, n)
// if row is an array and cell is a number, return that; else throw error
if (this[m] instanceof Array && typeof this[m][n] === 'number')
return this[m][n]
throw new ReferenceError('No entry found.')
/** The Ackermann Function */
function ack(m, n) !Number.isFinite(m + n))
throw new RangeError('Only natural numbers allowed.')
/** The returned result (the answer) */
let returned;
try
// try to get a database entry, if it exists
returned = db.get(m, n)
catch (err)
// else, do Ackermann’s algorithm
if (m === 0)
returned = n + 1
else if (n === 0)
returned = ack(m - 1, 1)
else
returned = ack(m - 1, ack(m, n - 1))
// make a new database entry for the result
db.set(m, n, returned)
return returned
answered 14 mins ago
chharveychharvey
1015
1015
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f142304%2fcomputing-the-ackermann-function%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown