Xorting an array
Pyth, 40 36 31 30 bytes
Ju.|G^2slHxMf>FT.:Q2Z|tSIxRJQJ
Try it online: Demonstration or Test Suite
Each of the big test-cases finishes in a couple of seconds.
Explanation:
First I'll explain the method and why it works. I'll do this with the example list: [7, 2, 13, 9]
.
The first two numbers are already wrong (7 > 2
). We want to xor with some number to change that inequality symbol (7 xor X < 2 xor X
). Since xor operates on the binary representations, lets look at them.
7 = 1 1 1
2 = 1 0
When we apply xor with some number to each number, the value at some positions will change. If you change the values at the first position (2^0
), the inequality symbol doesn't change. The same thing happens, when we change the values at the second position (2^1
). Also the symbol won't change if we change values at the fourth, fifth, ... positions (2^3
, 2^4
, ...). The inequality symbol only changes direction, if we change the third position (2^2
).
7 xor 2^0 = 1 1 0 7 xor 2^1 = 1 0 1 7 xor 2^2 = 1 1 7 xor 2^3 = 1 1 1 1
2 xor 2^0 = 1 1 2 xor 2^1 = 0 2 xor 2^2 = 1 1 0 2 xor 2^3 = 1 0 1 0
6 > 3 5 > 0 3 < 6 15 > 10
If we change multiple positions at once, of course the same thing happens. If any of the positions we change is the third, than the inequality symbol changes, otherwise not.
The next pair is already sorted: 2 < 13
. If we look at the binary representation, we notice, that we can xor anything to it and the inequality symbol is still correct, except when we change the fourth position (2^3
).
2 = 1 0 2 xor 2^3 = 1 0 1 0
13 = 1 1 0 1 13 xor 2^3 = 1 0 1
2 < 13 10 > 5
So we don't want to change the fourth position. For the next pair we want to change something, since 13 > 9
. Here we again have to change the third position.
13 = 1 1 0 1 13 xor 2^2 = 1 0 0 1
9 = 1 0 0 1 9 xor 2^2 = 1 1 0 1
13 > 9 9 < 13
Now recap: To end up in a sorted list, we again have to change the third position and don't want to change the fourth position. All other positions doesn't matter. The smallest number is simply 4 = 0100
. Other choices would be 5 = 0101
, 6 = 0110
, 7 = 0111
, 20 = 10100
, 21 = 10101
, ...
Xoring with 4
will result in the list [3, 6, 9, 13]
, with 6
will get [1, 4, 11, 15]
, and with 21
will get [18, 23, 24, 28]
.
So for a list, we need to find the positions, that will change the inequality symbol if it points into the wrong direction. We find the position simply by taking the most significant bit of the xor of the pair. We combine all these position (with or) to result in a candidate number. The we check, if we haven't accidentally destroyed the already sorted pairs.
Ju.|G^2slHxMf>FT.:Q2Z implicit: Q = input list
.:Q2 all substrings of length 2
f>FT filter for pairs that are in descending order
xM apply xor to each such pair
u Z reduce this list, start value G = 0
iteration value is H
^2slH 2 to the power of floor(logarithm base 2 of H)
this gives a mask representing the most significant bit
.|G update G with the bitwise or of G and ^
J store the result in J
|tSIxRJQJ
xRJQ xor each element of the input list with J
SI check if the list is sorted
t subtract 1
| J this number or (if equal to zero) J
implicit print
Ruby 2, 119
->a,*o{a.each_cons(2){|x,y|x==y||o[i=(x^y).bit_length-1]==1-(o[i]=x[i])&&(return-1)};(o.map(&:to_i).reverse*'').to_i 2}
Runs in 42 milliseconds on the large test cases.
Ungolfed:
def first_differing_bit(a,b)
(a^b).bit_length - 1
end
def xort(ary)
required_bits = []
ary.each_cons(2) do |a,b|
i = first_differing_bit(a,b)
if i > -1
bit = a[i]
if required_bits[i] && required_bits[i] != bit
return -1
else
required_bits[i] = bit
end
end
end
required_bits.map(&:to_i).reverse.join.to_i(2)
end
For once I wrote the ungolfed version first, then golfed it, since figuring out the right algorithm was a challenge in itself.
I actually tried to write something like this a few years ago to make a binary tree structure that would locally self-balance by letting each node redefine its comparison function dynamically. At first I thought I could just use xor, but as you say for random data there's unlikely to be a viable value.
Julia, 174 144 77 75 71
[EDIT] Thanks to Alex A. for anonymizing & various shorthands.
[EDIT 2] Replaced my own implementation by the builtin issorted()
.
Runs in linear time and handles the big files without noticeable delay. Works just as well for negative numbers.
l->(r=0;s=issorted;for d=63:-1:0 s((l$r).>>d)||(r$=2^d)end;s(l$r)?r:[])
Another variant that calculates the result closest to a given key (the above returns the smallest).
(l,r)->(s=issorted;for d=63:-1:0 s((l$r).>>d)||(r$=2^d)end;s(l$r)?r:[])
usage:
julia> xort = l->(r=0;s=issorted;for d=63:-1:0 s((l$r).>>d)||(r$=2^d)end;s(l$r)?r:[])
(anonymous function)
julia> xort([4 7 6 1 0 3])
5
Example, step by step: [4 7 6 1 0 3] => 5
Start with:
4 0b0100
7 0b0111
6 0b0110
1 0b0001
0 0b0000
3 0b0011
result 0b0000
If the first n bits are sorted, do nothing.
0b0
0b0
0b0
0b0
0b0
0b0
result 0b0000
^
If the first n bits are not sorted, flip the nth bit.
0b01 0b00
0b01 0b00
0b01 0b00
0b00 => 0b01
0b00 0b01
0b00 0b01
result 0b0000 0b0100
^ ^
0b000
0b001
0b001
0b010
0b010
0b011
result 0b0100
^
0b0000 0b0001 1
0b0011 0b0010 2
0b0010 0b0011 3
0b0101 => 0b0100 4
0b0100 0b0101 5
0b0111 0b0110 6
result 0b0100 0b0101 5
^ ^
If the bit flip does not sort the truncated integers, xorting is
impossible. We continue anyway and check for success in the end.