Find a function with cycles of every length
MATL, 47 bytes
E|G0<-QXJ:tQ*2/0hStJ<f0))Q2MQ)&:J6M-)2/ttk>Eq*k
Try it online!
General explanation
Function 2 below is the same as used in @Sp3000's answer to the related challenge. Thanks to @Agawa001 for noticing.
The function is the composition of three:
- Bijection from Z (the integers) to N (the naturals).
- Bijection from N to N with the desired characteristic (one cycle of each length).
- Inverse of function 1.
Functions 1 and 3 are used because it's easier (I think) to achieve the desired behaviour in N than in Z.
Function 2 is as follows: upper line is domain, lower line is codomain. Commas are used for clarity:
1, 2 3, 4 5 6, 7 8 9 10 ...
1, 3 2, 6 4 5, 10 7 8 9 ...
The first block (from upper 1
to lower 1
) is a cycle of length 1. The second (from 2 3
to 3 2
) is a cycle of length 2, etc. In each block, the lower part (image of the function) is the upper part circularly shifted one step to the right.
Function 1 is as follows:
-5 -4 -3 -2 -1 0 +1 +2 +3 +4 ...
+10 +8 +6 +4 +2 +1 +3 +5 +7 +9 ...
Function 3 is the same as 1 with the two lines swapped.
Examples
The image of 3
is -5
. First 3
is mapped to 7
by function 1; then 7
is mapped to 10
by function 2; then 10
is mapped to -5` by function 3.
The length-1 cycle is 0
. The length-2 cycle is -1 1
. The length-3 cycle is -3 2 -2
, etc.
Code explained
Functions 1 and 3 are straightforward.
Function 2 works by finding the lower endpoint of the corresponding input block. For example, if the input to this function is 9
it finds 7
(see blocks above). It then picks the upper endpoint, which is 10
in the example. The circular shift of the block is achieved thanks to MATL's 1-based, modular indexing.
% FUNCTION 1
% Implicit input
E| % Multiply by two. Absolute value
G0< % 1 if input is negative, 0 otherwise
- % Subtract
Q % Add 1
XJ % Copy to clipboard J. Used as input to the next function
% FUNCTION 2
: % Range [1 2 ... J], where J denotes the input to this function
tQ* % Duplicate, increment by 1, multiply
2/ % Divide by 2
0hS % Prepend a 0. This is needed in case J is 0
tJ<f % Duplicate. Find indices that are less than the input J
0) % Pick the last index.
) % Apply as index to obtain input value that ends previous block
Q % Add 1: start of current block
2M % Push the two arguments to second-to-last function call
Q) % Add 1 and use as index: end of current block
&: % Inclusive binary range: generate input block
J % Push J (input to function 2)
6M- % Subtract start of block
) % Apply as index (1-based, modular). This realizes the shifting
% FUNCTION 3
2/ % Divide by 2
ttk> % Duplicate. 1 if decimal part is not 0; 0 otherwise
Eq % Multiply by 2, add 1
* % Multiply
k % Round down
% Implicit display
Python 2, 55 bytes
g=lambda n,k=1:n/k and~g(~n+k*(n>0),k+1)+k*(n>0)or-~n%k
59 bytes:
g=lambda n,k=1:n<0and~g(~n,2)or n/k and k+g(n-k,k+2)or-~n%k
Creates the cycles
[0]
[-1, -2]
[1, 2, 3]
[-3, -4, -5, -6]
[4, 5, 6, 7, 8]
...
Modified from my solution on the earlier challenge, which is modified from Sp3000's construction.
The function
g=lambda n,k=1:n/k and k+g(n-k,k+2)or-~n%k
makes odd-size cycles of non-negative numbers
[0]
[1, 2, 3]
[4, 5, 6, 7, 8]
...
To find the correct cycle size k
, shift the input n
down by k=1,3,5,7,...
until the result is in the interval [0,k)
. Cycle this interval with the operation n->(n+1)%k
, then undo all the subtractions done on the input. This is implemented recursively by k+g(n-k,k+2)
.
Now, we need the negative to make the even cycles. Note that if we modify g
to start with k=2
in g
, we'd get even-size cycles
[0, 1]
[2, 3, 4, 5]
[6, 7, 8, 9, 10, 11]
...
These biject to negatives via bit-complement ~
. So, when n
is negative, we simply evaluate g(n)
as ~g(~n,2)
.
Python 3, 146 bytes
For every number greater than 0, there are even loops (len 2,4,6,8...), and less than 0, odd loops (1,3,5,7). 0 maps to 0.
(-3,-2,-1),(0),(1,2),(3,4,5,6)
maps to
(-2,-1,-3),(0),(2,1),(6,3,4,5)
f=lambda x:1+2*int(abs(x)**.5)if x<1 else 2*int(x**.5+.5)
x=int(input());n=f(x)
if x>0:b=n*(n-2)/4
else:b=-((n+1)/2)**2
print(b+1+(x-b-2)%n)
Edit: @TuukkaX took off 8 bytes from the previous solution. And another 3.