Moore's law is just an observation and not a real law like the Law of Gravity.
Over the years, developers have taken the increasing CPU speeds and Memory sizes for granted. Here's a warm-up interview question that I ask every candidate.
Let's assume the working memory for your function is 8-Bytes. You are given two 32-Bit Integers and you need to swap them. In other words, how can you swap two variables without using a third?
Please take your time to solve this and refrain from looking up solutions online or for answers below. This is your first step in becoming a Computer Scientist!
Top comments (122)
Bitwise operators for the win.
This is a rather beautiful "bit" of problem solving :)
It will work for both integers and floating points, positive and negative, and there is no issue with overflow. If someone can figure this out on their own, I think that's quite impressive. Most of us just know it from having seen it before though.
That's true. My initial solution to it was using addition/subtraction and once I got the concept of diffs, I was able to deduce a solution using bit manipulation.
JavaScript, by nature, stores all numbers as double-precision IEEE 754 floating point numbers. The 52 bits of the fractional portion is used to store integers. This results in 'maximum safe integer' of 2^53 - 1. Further complicating things is that JavaScript's bitwise operators only deal with the lowest 32 bits of JavaScript's 54-bit integers. So the bitwise approach will not work on big integers in JavaScript.
I guess BigInt new primitive proposal will do the trick :)
The swap with bitwise operators will be the fastest at the hardware level. :)
Rather disappointingly, in most modern hardware it's the one with the temp variable that is fastest.
Depending on the architecture and compiler, they end up using three registers to do a swap under the hood I think.
Yes, but the idea is that you do this in assembly. It was a common trick because it saved a register and (at worst) it ran in the same number of cycles.
When I started going through the comments, I was actually thinking "what about bitwise xor?" And sure enough, here it is.
^,^
Yes. Whenever you can achieve something using bitwise operations I always prefer that.
x, y = y, x #
To be a little bit sneaky, use part of the function itself as swap space. This would only work if that space could be after the return instruction.
In pseudo-C:
This isn't thread-safe though, and a decent operating system might throw exceptions if you try to have executable code modify itself.
The
NOP
s don't actually give you any memory. ThoseNOP
s are wherever the executable image was loaded in memory, and the variableyep
is on the stack. Assuming your standard C memory model.I misread the post on the phone, but I see what the code is doing. Although, I'm not sure what assigning to NOP does?
C memory layout:
(source)
The function code itself is in the
text
segment. The variables for a particular invocation of the function are on the stack. Padding out the function withNOP
creates space within the function, which I'm using as a variable. Basically I'm interpreting the "working memory" part of the post to mean "variables allocated on the stack". Now that I think of it, what I'm doing is basically a static variable, which (according to the link) C puts in theinitialized data
segment if you do it the right way, but then it would legitimately be a variable so I'd lose.It's probably not a legit way to do things (and might be impossible if the text segment is read-only as the link says it might be), but people had already posted the legit ways.
Thanks for that explanation. It's been a while since I've written C/C++, but this approach makes sense. I love the thought process of cleverly using that available memory, even if it doesn't work out. 👏👏👏
The variable
yep
is declared within a function body without astatic
qualifier, which means it's what the C standard calls an automatic storage duration variable. This means its duration is between the curly braces, and most compilers will use a register or put it on the stack.Being completely nitpicky here, but the variable
yep
is declared after its use and also without a type. I'm sure modern compilers wouldn't even let you do that.Modern operating systems load the
text
segment into a virtual memory page marked as read-only. So yes, even if this compiles, it will generate a segfault on modern operating systems.yep
isn't a variable, though, it's a label, representing an offset from the start of the function.I agree compilers and OS's will tend to prevent this. I said as much before, and trying to create a proof of concept in assembly has born that out.
It wasn't a completely fruitless exercise though, as it was a chance to learn some assembly, which provided some insights about the challenge that I might make a post about. Basically, swapping using math might not actually be more memory-efficient (however you define that, and without the kind of cheating I've been talking about) than swapping using "a variable".
It creates more memory though.
Tricky bit: It can cause integer overflow if
x + y > int.MAX
assuming cyclic overflows, i.e. INTMAX + 1 = INTMIN:
if
INTMAX - y < x
(equivalent tox + y > INTMAX
without cycles)then
x + y = INTMIN + y - (INTMAX - x - 1)
ergo, rewriting the equations as
x' = x + y
y' = x' - y
x'' = x' - y'
we get:
x' = INTMIN + y - (INTMAX - x - 1)
y' = x' - y
y' = INTMIN - (INTMAX - x - 1)
y' = INTMIN + 1 - INTMAX + x
y' = x
x'' = INTMIN + y - (INTMAX - x - 1) - y'
x'' = INTMIN + y - (INTMAX - x - 1) - x
x'' = INTMIN + y - INTMAX + x + 1 - x
x'' = INTMIN + 1 + y - INTMAX
x'' = y
Overflows schmoverflows.
EDIT: Might be worth noting that this approach and the bitwise operator are essentially the same thing. Combine two things in a way that is reversible if you have either of the two originals.
Unfortunately that works in theory but in practice and most compilers do not guarantee that behavior. For example:
Java: Will back cycle to -2147483648
Javascript: will eventually print infinity if the value is too high
Go: Will not compile
That's the second step in becoming a computer scientist.
limit x and y to 16 bit or store 32-bit numbers in 64-bit. max int for 64-bit signed or unsigned is twice that of it's 32-bit counterpart.
You cannot overcome size problem if it's expressed in bits. Only higher-order structures like arrays would enable that and then you are absolutely storing more than two variables (possibly more than one per entry if it's a pointer).
That defeats the purpose of doing that plus you are making a difficult work of complicating things. I suspect is better if you use a temp variable after all!
I suspect it's better if you don't try to copy with only two memory areas, but if you're going to worry about overflowing, then doubling storage size is the least complex thing you could do, and compatible with various CPU operating modes.
Only two options, either using memory or CPU
like XOR swap (bullet-proof solution)
Or addition & subtraction (which is not bullet-proof when dealing with a 32-bit register that would need extra care with overflow)
XOR swap is NOT bullet proof, you need to check if x == y first. If x == y, then the function sets both values to zero.
No, it will not. Let set x=5, y=5.
It is a nop actually, but still correct.
Since xor and subtraction were already mentioned, may I chime in with a circular shift over the 64 bit value of the whole function memory?
This one is different, but how exactly would you implement it in a programming language?
Basically, it works like this (x64 assembler):
mov rax, [m64]
ror rax, 32
mov [m64], rax
No, I mean on a high level programming language which you have no idea where is stored.
Even for an assembly, the two variables could potentially be stored in two different registers. But we're going to assume not!
Cool idea anyways
I love you
That's awesome. I did not know about it. I wonder if it swaps values instead of pointters.
Found this on SO @ stackoverflow.com/questions/445179....
So now we've reduced the problem to "How do you swap the two topmost stack items?" :)
Very contrived, only works on ARM, not even acceptable by all C compilers, but you never constrained the problem that way!
I went to bed, and realized that my answer is wrong. Exchanging the values in register is completely invisible to the function caller. Here's the correct answer.
Further, as
R0-R3
are all caller-save registers in the ARM ABI, the function body, excluding the function entry stack shenanigans, uses zero memory.Oh and as prplz mentioned, any self-respecting compiler nowadays will use only registers any way you decide to implement this little stub of a function. Yes yes, the question is very contrived too, but hey.
Aren't you using a third register though?
Are you're thinking that you can do that because it's not in RAM?
The way the question is formulated, you're limited by having a certain amount of memory. Any register use doesn't count against that at all.
As funny as it is, you declare a new pointer to the
swap
function allocating new memory.I've written my answer with C in mind, and it does explain how it works if you try the calculation by hand: onlinegdb.com/B1IY6hFK7
When both
a
andb
has the same value: onlinegdb.com/r1WeypFF7Result may vary depending on the language compiler, you might have to adjust the snippet according to your language's nuances.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.