First and foremost, your toolset for programming needs a style formatter. Common formatters include clang-format and AStyle.
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <functional>
Get in the habit of organizing your headers. When you have a long list of them, nobody wants to search them linearly. I recommend organizing them into ascending groups of code quality, with each group sorted alphabetically.
- Prototype/Interface header for this implementation (if applicable).
- Other headers from the same project.
- Non-standard headers from non-popular vendors (Mnmlstc, CppIterTools)
- Non-standard headers from popular vendors (QT, Folly, EASTL, ABSL)
- Boost headers
- C++ Library headers (
<iostream>
, <vector>
, <cstdint>
)
- C Library headers.
Only include what you need. You don't need <iostream>
.
// at least two digits must be distinct
bool isUnique(int num) {
constexpr int numlength = 4;
std::vector<int> digits;
for (int i = 0; i < numlength; ++i) {
int digit = num % 10;
digits.push_back(digit);
num /= 10;
}
// If digits are all equal return false.
if (std::adjacent_find(digits.begin(), digits.end(),
std::not_equal_to<int>()) == digits.end()) {
return false;
} else {
return true;
}
}
The fact that you had to include a comment to describe what this function does should be a clue that you should pick a better name for this function.
Why does isUnique()
care about the length of the number? A number length of 4 is an implementation detail of your Kaprekar calculation. isUnique
should only be concerned with checking if there is a non-unique digit.
Don't be afraid to write more functions. Functions should be short and simple. You have two sections in this one function: int-to-char conversion and distinct element checking.
The expression in the if
statement at the end generates a boolean. Instead of branching, just return the result of the expression.
bool hasNonUniqueChar(std::string const& str) {
return std::adjacent_find(str.begin(), str.end(), std::not_equal_to())
!= str.end();
}
bool hasNonUniqueDigit(int num) {
auto const digits = std::to_string(num);
return hasNonUniqueChar(digits);
}
int KaprekarsConstant(int num) {
KaprekarsConstant
is probably going to make people think this is a constant value being returned, not the number of iterations. Consider renaming the function appropriately.
constexpr int kaprekarsConst = 6174;
constexpr int lengthRequirement = 4;
Don't think you really need either of these. Kaprekar can be calculated for any number of digits. Comparing to a known result seems like cheating. What you should be calculating is the number of iterations to the point a cycle happens.
static int iteration = 0;
Why static
? If the callee wants an accumulation of all the iterations, they can add it up themselves.
if (std::to_string(num).length() == lengthRequirement && isUnique(num)) {
You convert the number to a string here, then convert it again in isUnique
. You are throwing away work you've already done.
int difference = num;
std::string sNum;
while (difference != kaprekarsConst) {
Again, this is (a) cheating and (b) a problem. Again, you want to calculate values until you find a value you've previously encountered. For 3-digit and 4-digit values, they converge to one value and then infinitely repeats.
sNum = std::to_string(difference);
I said previously that comparing to \$6174\$ was a problem. Consider the input \$2111\$.
$$2111 - 1112 = 999$$
Notice that \$999\$ is no longer a 4-digit number. That's an issue. Continuing,
$$999 - 999 = 0 \\
0 - 0 = 0$$
See the problem with comparing to \$6174\$? We've converged to \$0\$ and are now stuck in an infinite loop. There are \$77\$ four-digit numbers that converge to \$0\$ if leading zeros are discarded. If we preserved those leading-zeros, as Kaprekar did in the original formulation, then
$$2111 - 1112 = 0999 \\
9990 - 0999 = 8991 \\
9981 - 1899 = 8082 \\
8820 - 288 = 8532 \\
8532 - 2358 = 6174 \\
7641 - 1467 = 6174
$$
std::sort(sNum.begin(), sNum.end(), std::greater<int>());
int ascendNum = stoi(sNum);
Make sure you namespace qualify symbols and avoid using namespace std;
.
std::sort(sNum.begin(), sNum.end(), std::less<int>());
How about std::reverse
?