skip to content

With XOR, how do you recover both values when two sequence numbers are dropped?

level: seniorimportance: nice to knowfreq 30%

answer

  1. one fold gives their combination, not either
  2. what a set bit in that combination means
  3. find a position where they disagree
  4. split the whole stream on that bit
  5. duplicates land in one bucket together

basics

~20 s

One fold gives the combination of the two missing numbers, and any bit set in it marks a position where they disagree. Splitting the whole stream on that bit puts one in each half, so folding each half separately recovers both.

solid answer

~50 s

A single fold over the expected range plus the received log leaves `d`, the combination of the two dropped numbers. That is not either answer, but it is the key to getting them: `d` is nonzero precisely because the two numbers are distinct, and every bit set in `d` marks a position where they differ. Pick one such bit — the lowest is convenient — and use it as a partition rule. Every value in the stream falls into the bucket where that bit is set or the bucket where it is clear. Any number that appears twice contributes both occurrences to the *same* bucket, so it still cancels; the two dropped numbers land in *different* buckets, because they disagree on exactly that bit. Two independent accumulators, one per bucket, therefore end holding one dropped number each. Still one or two passes, still constant space.

code

pseudocode · 13 lines
pseudocode
d = 0
for i in 0..length(s)-1:
    d = d xor s[i]
// d is the combination of the two missing values, nonzero

bit = lowest_set_bit(d)
p = 0
q = 0
for i in 0..length(s)-1:
    if (s[i] AND bit) != 0:
        p = p xor s[i]
    else:
        q = q xor s[i]

go deeper

for a junior

Know that a single fold over a stream with two unpaired values returns their combination rather than either one, and that recovering both takes a second step.

for a middle

Explain that a set bit in the combination marks a position where the two differ, and describe the partition it induces. Be able to walk the two-accumulator version on a small example.

for a senior

Justify why the partition preserves cancellation for every paired value, check explicitly that the combination is nonzero, and state the cost: O(n) time, constant space, order-independent, shardable.

for a principal

Judge when constant-space cleverness is worth it at all. Be ready to say why this stops at two, and why recovering more unpaired values usually argues for keeping counts instead of stacking parity tricks.

## Where the single fold stops Folding the expected range together with the received log gives an accumulator in which every delivered number appears twice and cancels. With one number dropped, the accumulator is that number. With two dropped — call them `x` and `y` — the accumulator is `d = x xor y`. That value identifies neither: for distinct sequence numbers it is typically not even a legal sequence number. A candidate who stops here has the right first step and no second one. ## The distinguishing bit The second step is to notice what `d` actually tells you. Bit by bit, `d` has a 1 exactly where `x` and `y` disagree and a 0 exactly where they agree. Two facts follow: - **`d` is nonzero.** If it were zero, `x` and `y` would agree everywhere, meaning they are the same value. Distinct sequence numbers cannot be, so at least one bit of `d` is set. (If your inputs *can* repeat a value, this is the precondition to check first — the technique has nothing to work with when `d` is zero.) - **Any set bit separates them.** Choose one such position and call the test "is this bit set?". `x` answers one way and `y` answers the other, necessarily. The lowest set bit is the conventional choice simply because it is cheap to obtain, but any of them works and none is better than another. ## Why partitioning preserves the cancellation This is the part worth saying out loud, because it is the step that makes the technique correct rather than merely clever. Partition the entire stream — expected values and received values alike — using the chosen bit as the test. A number that appears twice has the same bit pattern both times, so both of its occurrences answer the test identically and land in the same bucket. It therefore still appears an even number of times *within that bucket* and still cancels. Nothing paired ever straddles the partition. The two dropped numbers, by construction of the bit, land in opposite buckets. So each bucket contains many even-multiplicity values and exactly one odd-multiplicity value, which is precisely the situation a plain fold solves. Two accumulators, one per bucket, end holding `x` and `y`. ## Cost and shape One pass to compute `d`, one pass to partition and fold — or a single pass if the stream can be replayed only once and you are willing to buffer nothing, by computing `d` over the expected range in closed form and the received log in the same pass. Time is O(n); extra space is three accumulators plus the chosen bit, so O(1). The order of the input never matters, and because the combining operation is associative, both passes shard cleanly: compute partial accumulators per shard and combine them. Which value ends up in which accumulator is not determined by anything meaningful — you get the pair, unordered. If the caller needs to know which of the two is which, that information has to come from elsewhere. ## Where it stops generalising The natural next question is three unpaired values, and the honest answer is that this construction does not extend. With three, the fold gives `x xor y xor z`, and a set bit in that value no longer guarantees a clean split — it tells you an odd number of the three have the bit set, which is one or all three, and the "all three" case leaves one bucket with three odd-multiplicity values and the other with none. Recovering `k` unpaired values in constant space needs a different instrument altogether — a family of independent parity checks rather than a single one — and at that point the memory saved rarely justifies the machinery over simply keeping counts. ## What the interviewer is testing Not the trick itself, which is memorisable. The signal is whether the candidate can state *why* the partition preserves cancellation, and whether they check that `d` is nonzero before relying on a set bit existing. Those two statements are the difference between having read the technique and understanding it.

  • Why does partitioning the stream not break the cancellation of the paired values?
    A number that appears twice has the same bit pattern both times, so both occurrences answer the partition test identically and fall into the same bucket. Its multiplicity within that bucket is still even, so it still cancels. No paired value can straddle the split.
  • Does it matter which set bit of the combination you pick?
    No. Every set bit is a position where the two values disagree, so any of them separates the pair. The lowest set bit is conventional only because it is the cheapest to obtain; correctness is identical for any choice.
  • Does the same construction extend to three unpaired values?
    No. With three, a set bit in the fold means an odd number of them have that bit — one or all three. In the all-three case one bucket holds three odd-multiplicity values and the split has bought nothing. Recovering more than two in constant space needs several independent parity checks, not one distinguishing bit.

saying these in an interview costs you the question

  • Reports the combined value as if it were an answer
  • Partitions on a bit where the combination is zero
  • Assumes the two values differ in the highest bit
  • Forgets to check that the combination is nonzero
  • Claims paired values can split across both buckets
  • Says the technique scales to any number of unpaired values

context