Consider the following spiral of positive integers:
We now define grambulation as a binary operation \$\lozenge : \mathbb N \times \mathbb N \to \mathbb N\$, using this grid. Some example inputs and outputs for grambulation are:
\begin{align*} 1 & \lozenge 9 = 25 \\ 1 & \lozenge 2 = 11 \\ 11 & \lozenge 10 = 25 \\ 9 & \lozenge 1 = 5 \\ 19 & \lozenge 4 = 13 \\ 76 & \lozenge 6 = 62 \\ 17 & \lozenge 17 = 17 \\ 62 & \lozenge 55 = 298 \end{align*}
Feel free to try to figure out the pattern before continuing.
How to grambulate \$x\$ and \$y\$
The two coordinates of the inputs, \$x \lozenge y\$, in the spiral grid above are found, where \1ドル\$ is located at \$(0, 0)\$, \2ドル\$ at \$(1, 0)\$ and so on. For \$x \lozenge y\$, call these coordinates \$x'\$ and \$y'\$. You then find the vector from \$x'\$ to \$y'\$, and calculate the coordinates found by applying this vector to \$y'\$.
A worked example: \3ドル \lozenge 11 = 27\$. First, we calculate our coordinates: $$x' = (1, 1), y' = (2, 0).$$ Next, we see that the vector from \$x'\$ to \$y'\$ is \$\vec v = (+1, -1)\$. Finally, we add this to \$y'\$ to get the coordinate \$(3, -1)\$, which is the coordinates of \27ドル\$.
Alternatively, a visual demonstration:
visual example with arrows from 3 to 11 and 11 to 27
Note from the \62ドル \lozenge 55 = 298\$ example above, our spiral is not limited to integers below \121ドル\$, and in fact, this binary operation is well defined for all pairs of positive integers.
Some properties of grambulation:
\$x \lozenge x = x\$
\$x \lozenge y = z \iff z \lozenge y = x\$
It is non-associative and non-commutative
It is non-injective but is surjective
\$x^2 \lozenge y^2 = z^2\$ where \$x < y\$ and \$x, y, z\$ all have the same parity
Additionally, \$x^2 \lozenge y^2 = z^2 - 1\$ where \$x\$ and \$y\$ have different parities
Further, \$x^2 \lozenge (x + 2c)^2 = (x + 4c)^2\$ for \$x, c \in \mathbb N\$.
Given two positive integers \$x, y \in \mathbb N\$, output \$x \lozenge y\$. You may take input and output in any reasonable format or method, and this is code-golf, so the shortest code in bytes wins.
Note that the order of inputs does matter (\$x \lozenge y \ne y \lozenge x\$ for most \$x, y \in \mathbb N\$), but you may take input in either consistent order.
Test cases
x y x◊y
1 9 25
1 2 11
11 2 1
11 10 25
9 1 5
19 4 13
76 6 62
17 17 17
62 55 298
3 11 27
16 400 1296
182 240 306
249 1 281
281 1 249
32 98 196
88 16 202
60 41 210
59 85 227
And, a visual guide for the last few, on a larger grid:
grid with 15 colored cells, indicated a visual guide for the last 5 test cases
10 Answers 10
MATL, 22 bytes
sQ1YLXIG"I@=&fh]Ew-X{)
As a bonus, see the beautiful pattern of the function for all pairs of inputs from 1 to L. MATL Online times out for L moderately large, but here's the result for L=100 (click here for a view with better resolution):
How it works
% Implicit input: row vector of two numbers.
sQ % Sum, add 1. This is enough for the size of the spiral
1YL % Matrix with spiral of that size
XI % Copy into clipboard I
G" % For each number in the input
I % Push the spiral again
@= % True for the entri that equals the number
&fh % Row and column indices of that entry as a vector of two numbers
] % End
E % Multiply by two, element-wise
w- % Swap, subtract. Gives the coordinates of the result
X{ % Convert to cell array, for indexing
) % Index into the spiral
% Implcit display
The version with graphical input is as above but replaces the input by a loop over all pairs of values from 1 to L. The results are concatenated and reshaped as a square matrix, which is displayed with the autumn colormap.
-
1\$\begingroup\$ Just out of curiosity, does MATL display the y-axis like that by default? I took me a bit to realize it was flipped from the convention. \$\endgroup\$2023年04月06日 18:00:08 +00:00Commented Apr 6, 2023 at 18:00
-
\$\begingroup\$ @WheatWizard Ah, yes, it follows MATLAB's convention that images are displayed that way by default ("ij" or "matrix" axes mode). On the other hand, plots have the vertical axis increasing upwards, as usual ("xy" or "Cartesian" axes mode) \$\endgroup\$Luis Mendo– Luis Mendo2023年04月06日 20:25:06 +00:00Commented Apr 6, 2023 at 20:25
Pyth, (削除) 49 (削除ここまで) (削除) 32 (削除ここまで) 31 bytes
@Jsua_MCG+LheeGUGysQ]]1s-MPBxLJ
Takes input as [y, x].
Jsua_MCG+LheeGUGysQ]]1: Generate the spiral to a sufficient size, then collapse it to a flat list and save it inJ.xLJ: Find the index in J of the two inputs.s-MPB: Convert to the target index.@: Index intoJto get the target number.
Wolfram Language (Mathematica), 187 bytes
(t=(n=#;{1,-1}(Sum[#[.5π⌊(4k-3)^.5⌋],{k,n-1}]&/@{Sin,Cos}))&;(x=#1;y=#2;s=#1+#2;Which[x>y&&x>-y,(2x-1)^2+s,x<=y&&x>-y,4y^2-s+1,x<=y&&x<=-y,4x^2-s+1,True,(2y-1)^2+s])&@@(2t@#2-t@#1))&
Ungolfed self-explained:
Clear[toXY, fromXY, main];
toXY[n_] := {
Sum[
Sin[.5 π ⌊Sqrt[4 k - 3]⌋], {k,
n - 1}],
-Sum[
Cos[-.5 π ⌊Sqrt[4 k - 3]⌋], {k,
n - 1}]};
fromXY[{x_, y_}] := Which[
x > y && x > -y, (x*2 - 1)^2 + (x + y),
x <= y && x > -y, (y*2)^2 - (x + y) + 1,
x <= y && x <= -y, (x*2)^2 - (x + y) + 1,
True, (y*2 - 1)^2 + (x + y)
];
main[n1_, n2_] := fromXY[2 toXY@n2 - toXY@n1];
Raku, 74 bytes
{.first(2*.[$^b]-.[$^a]):k given 1⁄2,0,|[\+] flat (1,*i...*)Zxx(1,1.5...*)}
{ expr1 given expr2 }is the function body. It evaluatesexpr1after assigning the value ofexpr2to the$_topic variable.expr2here is an expression for the lazy infinite sequence of spiral coordinates, in the form of complex numbers.(1, *i ... *)is the sequence of1,i,-1, and-i, repeated infinitely.(1, 1.5 ... *)is the infinite arithmetic sequence 1, 1.5, 2, 2.5, 3, 3.5, ....ZxxZips the two preceding sequences together using thexxreplication operator:1 xx 1,i xx 1.5,-1 xx 2,-i xx 2.5, .... The half-integer replication numbers are rounded down, producing(1),(i),(-1, -1),(-i, -i),(1, 1, 1),(i, i, i), ....flatflattens that list of lists.[\+]scans that list with the addition operator+, producing the infinite list of partial sums. This is the spiral, except that it lacks the first origin element0.1⁄2, 0, |...pastes the origin0onto the front of the spiral, as well as another leading element1⁄2to make the whole thing 1-indexed instead of 0-indexed..[$^a]and.[$^b]are the coordinates in the spiral of the first and second arguments to the function, respectively.2 * .[$^b] - .[$^a]are the coordinates of the grambulated output..first(...):kfinds the first index (thanks to the:key adverb) within the spiral where the value is the coordinates found in the previous step.
JavaScript (ES6), 132 bytes
Expects (p)(q) and returns \$p\lozenge q\$.
p=>q=>((g=(t,h=o=[1],v=n=x=y=0)=>n==t|x+[,y]==t?x:g(t,(i=o[o[[x-=h,y-=v]]=++n,[x-v,y+h]])?h:v,i?v:-h))(q),Y=2*y,g([2*x-g(p),Y-y]),n)
How?
The helper function \$g\$ expects a target \$t\$ which is either the index or the coordinates of an element in the spiral.
Whenever \$g\$ is called, it starts at the origin and walks through the spiral until the target is reached, keeping track of its index \$n\$ and its position \$(x,y)\$ (all these variables being available in the global scope).
We first invoke \$g(q)\$ to get the coordinates \$(X,Y)\$ of the 2nd element, then \$g(p)\$ to get the coordinates \$(x,y)\$ of the 1st element, and finally \$g([2X-x,2Y-y])\$ to get the index of \$p\lozenge q\$.
Pyth, (削除) 65 (削除ここまで) (削除) 47 (削除ここまで) (削除) 45 (削除ここまで) (削除) 38 (削除ここまで) (削除) 34 (削除ここまで) (削除) 31 (削除ここまで) 30 bytes
-18 bytes by changing the loop structure
-2 bytes by altering some assignment
-7 bytes by switching to integer coordinates
-1 byte from swapping input order, -3 bytes from better list construction
-3 bytes by switching to imaginary coordinates
-1 byte from better representation of i
xJ+bm=+Z^.j).Ey@d2^sQ3_i@LJQ_2
Takes input as a list of integers [y,x]. Generates a list J of spiral coordinates in the imaginary plane, then gets the index in J for 2*J[y] - J[x]. To generate this list, we take advantage of the fact that ceil(2*sqrt(x)) stays the same values for increasingly long intervals, in the exact same way that the direction of travel when traversing this kind of spiral does.
Explanation
# implicitly assign Z = 0 and Q = eval(input())
J # assign J to
m ^sQ3 # map range(sum(Q)**3) over lambda d
=+Z # add and assign to Z
^.j) # i to the power of
.E # ceiling
y@d2 # 2 * sqrt(d)
+b # prepend a char (to offset list)
xJ # find the index in J of
@LJQ # map x and y to their coordinates in J
_i _2 # double the first and subtract the second
Wolfram Language (Mathematica), (削除) 60 (削除ここまで) (削除) 59 (削除ここまで) 58 bytes
1//.i_/;{1,-2,1}.Tr/@+I^⌈2√Range[{##,i}-1]⌉!=0:>i+1&
Input [x, y].
Charcoal, 68 bytes
NθNη≔0ζ≔0εW¬ε«⊞υ⟦ij⟧≧+⊗¬ΣKD2✳+2ζζ✳ζ1¿›Lυ⌈⟦θη⟧≔⊕⌕υE§υ⊖η−⊗κ§§υ⊖θλε»⎚Iε
Try it online! Link is to verbose version of code. Explanation:
NθNη
Input x and y.
≔0ζ
Start in an arbitrary direction.
≔0ε
Start not knowing x◊y.
W¬ε«
Repeat until x◊y is known.
⊞υ⟦ij⟧
Save the current position.
≧+⊗¬ΣKD2✳+2ζζ
Turn if this is a corner.
✳ζ1
Take one step.
¿›Lυ⌈⟦θη⟧
If we know x' and y', then...
≔⊕⌕υE§υ⊖η−⊗κ§§υ⊖θλε
... see if we can find x◊y.
»⎚Iε
Output x◊y.
I tried using complex numbers but that also took 68 bytes:
×ばつI1jδ≧+δε¿›Lυ⌈⟦θη⟧≔⊕⌕υ−⊗§υ⊖η§υ⊖θζ»Iζ
Attempt This Online! Link is to verbose version of code. Explanation:
NθNη
Input x and y.
≔0ζ
Start not knowing x◊y.
≔0ε≔1δ
Start at the origin in an arbitrary direction.
W¬ζ«
Repeat until x◊y is known.
⊞υε
Save the current position.
×ばつδI1j
If this is a corner, then...
×ばつI1jδ
... rotate the direction.
≧+δε
Take one step.
¿›Lυ⌈⟦θη⟧
If we know x' and y', then...
≔⊕⌕υ−⊗§υ⊖η§υ⊖θζ
... see whether we know x◊y.
»Iζ
Output x◊y.
Scala, 395 bytes
395 bytes, it can be golfed much more. Golfed version. Try it online!
import scala.math._
type D=Double
def t(n:D)=(1 to n.toInt-1).map(k=>sin(.5*Pi*floor(sqrt(4*k-3)))).sum-> -(1 to n.toInt-1).map(k=>cos(-.5*Pi*floor(sqrt(4*k-3)))).sum
def f(p:(D,D))={val(x,y)=p
if(x>y&&x> -y)pow(2*x-1,2)+(x+y)else if(x<=y&&x> -y)pow(2*y,2)-(x+y)+1 else if(x<=y&&x<= -y)pow(2*x,2)-(x+y)+1 else pow(2*y-1,2)+(x+y)}
def m(n1:D,n2:D)={val(x,y)=t(n2);val(a,b)=t(n1);f((2*x-a,2*y-b))}
Ungolfed version. Try it online!
import scala.math._
object Main {
def toXY(n: Double): (Double, Double) = {
val x = (1 until n.toInt).map(k => sin(.5 * Pi * floor(sqrt(4 * k - 3)))).sum
val y = -(1 until n.toInt).map(k => cos(-.5 * Pi * floor(sqrt(4 * k - 3)))).sum
(x, y)
}
def fromXY(point: (Double, Double)): Double = {
val (x, y) = point
if(x > y && x > -y) pow(2*x - 1, 2) + (x + y)
else if(x <= y && x > -y) pow(2*y, 2) - (x + y) + 1
else if(x <= y && x <= -y) pow(2*x, 2) - (x + y) + 1
else pow(2*y - 1, 2) + (x + y)
}
def main(n1: Double, n2: Double): Double = {
val (x, y) = toXY(n2)
val (a, b) = toXY(n1)
fromXY((2*x - a, 2*y - b))
}
def main(args: Array[String]): Unit = {
println(main(182, 240))
}
}