Am I a Cullen Number?
x86_64 machine code (System V ABI), 28 27 bytes
-1 byte thanks to @Cody Gray, thanks!
A constant-time algorithm!
_cullen:
0: 0f bd cf bsrl %edi, %ecx
3: 0f bd c1 bsrl %ecx, %eax
6: 89 ca movl %ecx, %edx
8: 29 c2 subl %eax, %edx
a: 0f bd c2 bsrl %edx, %eax
d: 29 c1 subl %eax, %ecx
f: d3 e1 shll %cl, %ecx
11: ff c1 incl %ecx
13: 31 c0 xorl %eax, %eax
15: 39 f9 cmpl %edi, %ecx
17: 0f 94 c0 sete %al
1a: c3 retq
Explanation:
Let y an integer and x=y*2^y + 1
.
Taking logs, we have y + log2(y) = log2(x-1)
, thus y=log2(x-1)-log2(y)
. Plugging back the value of y, we get y=log2(x-1)-log2(log2(x-1)-log2(y))
. Doing this one more time, we obtain: y=log2(x-1)-log2[log2(x-1)-log2(log2(x-1)-log2(log2(x-1)-log2(y)))]
.
Let us remove the last terms (of the order of log2(log2(log2(log2(x))))
, this should be safe!), and assume that x-1≈x
, we obtain:
y≈log2(x)-log2[log2(x)-log2(log2(x))]
Now, letting f(n) = floor(log2(n))
, it can be verified manually that y
can be exactly retrieved by:
y=f(x)-f[f(x)-f(f(x))]
,
for y < 26, and thus x ⩽ 10^9, as specified by the challenge(1).
The algorithm then simply consists of computing y given x, and verify that x == y*2^y + 1.
The trick is that f(n)
can simply be implemented as the bsr
instruction (bit-scan reverse), which returns the index of the first 1-bit in n, and y*2^y
as y << y
.
Detailed code:
_cullen: ; int cullen(int x) {
0: 0f bd cf bsrl %edi, %ecx ; int fx = f(x);
3: 0f bd c1 bsrl %ecx, %eax ; int ffx = f(f(x));
6: 89 ca movl %ecx, %edx
8: 29 c2 subl %eax, %edx ; int a = fx - ffx;
a: 0f bd c2 bsrl %edx, %eax ; int ffxffx = f(a);
d: 29 c1 subl %eax, %ecx ; int y = fx - ffxffx;
f: d3 e1 shll %cl, %ecx ; int x_ = y<<y;
11: ff c1 incl %ecx ; x_++;
13: 31 c0 xorl %eax, %eax
15: 39 f9 cmpl %edi, %ecx
17: 0f 94 c0 sete %al
1a: c3 retq ; return (x_ == x);
; }
(1)In fact, this equality seems to hold for values of y up to 50000.
Jelly, 7 6 bytes
Ḷæ«`i’
Try it online!
Takes input as a command-line argument. If given a Cullen number C(n), outputs n+1 (which is truthy in Jelly, being an nonzero integer; note that we have n≥0 because the input is an integer, and Cullen numbers with negative n are never integers). If given a non-Cullen number, returns 0, which is falsey in Jelly.
Explanation
Ḷæ«`i’
Ḷ Form a range from 0 to (the input minus 1)
æ« Left-shift each element in the range by
` itself
i’ Look for (the input minus 1) in the resulting array
Basically, form an array of Cullen numbers minus one, then look for the input minus one in it. If the input is a Cullen number, we'll find it, otherwise we won't. Note that the array is necessarily long enough to reach to the input, because C(n) is always greater than n.
JavaScript (ES6), 37 35 bytes
Saved 2 bytes thanks to Neil
f=(n,k,x=k<<k^1)=>x<n?f(n,-~k):x==n
Demo
f=(n,k,x=k<<k^1)=>x<n?f(n,-~k):x==n
console.log(JSON.stringify([...Array(1000).keys()].filter(n => f(n))))