First of all, if you're unfamiliar with the Collatz sequence, it recursively applies the following function to an integer:
$$ f(n) = \begin{cases} n/2 & \text{if } n \equiv 0 \pmod{2}, \\ 3n+1 & \text{if } n \equiv 1 \pmod{2}. \end{cases} $$
What I wanted to do was to make a program that would:
- Quit if 0 was inputted
- Reprompt the user if something invalid was inputted
- For positive integers, e.g. 7, output "7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1"
- For negative integers, e.g. -3, where doing the above would output "-3 -8 -4 -2 -1 -2 -1 -2... [forever repeating]", output something like "-3 -8 -4 (-2 -1)", as "-2 -1" repeats forever.
This is what I have, which fits most of the criteria, but could definitely be improved somehow. What do you guys think?:
#include <stdio.h>
#include <stdbool.h>
long long cltz(long long x);
int main() {
long long n;
char input[256];
while (1) {
printf("Enter a positive or negative integer (0 to quit): ");
if (fgets(input, sizeof(input), stdin) == NULL) break;
if (sscanf(input, "%lld", &n) != 1) continue;
if (n == 0) break;
bool sign = n < 0;
long long on = n, slow = n, fast = n;
while (sign) {
slow = cltz(slow);
fast = cltz(cltz(fast));
if (fast == slow) break;
}
if (on == slow && sign) printf("( ");
printf("%lld ", n);
while (n != 1) {
n = cltz(n);
if (n == slow) break;
printf("%lld ", n);
}
long long c_slow = cltz(slow);
if (sign && on != slow) {
printf("( %lld ", slow);
while (c_slow != slow) {
printf("%lld ", c_slow);
c_slow = cltz(c_slow);
}
printf(")");
}
if (on == slow && sign) printf(")");
printf("\n");
}
return 0;
}
long long cltz(long long x) {
return (x & 1) ? ((x << 1) + x + 1) : (x >> 1);
}
(-2 -1)
but not the positive loop4 2 1
? \$\endgroup\$cltz
just stand for Collatz? I thought at first it was some bithack helper function since the name is very close toclz
(count leading zeros) andctz
(count trailing zeros). Or clear trailing zeros? But that doesn't make sense because clearing means zeroing. Anyway, see Why does C++ code for testing the Collatz conjecture run faster than hand-written asm? re: versions that will compile efficiently for x86, specifically that using an unsigned type (uint64_t
) was faster there, using/2
which has to truncate toward 0 for signed, unlike>>1
. \$\endgroup\$