ví dụ toy ghép thế này:
ban đầu:
x1 x2 x3 x4 x5 x6 x7 x8 x9 y1 y2 y3 y4 y5 y6 y7 y8 y9
ghép x9, x8, ... x2, x1 vào
trước slot của chỗ nó sau khi shuffle:
x1x1 x2
x2x3 x4
x3x5 x6
x4x7 x8
x5x9 y1
x6y2 y3
x7y4 y5
x8y6 y7
x9y8 y9
ở đây toy đi ngược x9, x8 để khỏi phải tách ví dụ x3 từ x2x3 nếu đi từ x1, x2 lên. Nhưng đi xuôi tách ra có khi lại lẹ hơn vì ko bị cache miss
(ghép vào sau cũng được, nhưng tới khi ghép y1, y2,... thì hơi lằng nhằng)
ghép y1, y2, ..., y9 vào trước slot của nó sau khi shuffle. Ở đây phải tách lấy y là phần sau của mỗi số ở slot
x1x1
y1x2 x2x3
y2x4 x3x5
y3x6 x4x7
y4x8 x5x9
y5y1 x6y2
y6y3 x7y4
y7y5 x8y6
y8y7 x9y8
y9y9
sau đó lấy phần trước của số ở mỗi slot là xong:
x1 y1 x2 y2 x3 y3 x4 y4 x5 y5 x6 y6 x7 y7 x8 y8 x9 y9
bước ghép y1, y2, ... có thể optimize gán thẳng luôn ko cần ghép:
x1x1
y1 x2x3
y2 x3x5
y3 x4x7
y4 x5x9
y5 x6y2
y6 x7y4
y7 x8y6
y8 x9y8
y9
để ghép 2 số a1 a2 với nhau thành a1a2 thì nếu biết trước a1, a2 <= n thì có thể ghép bằng cách nhân a1 với m>n rồi cộng a2: a1a2 = a1*m + a2. Ở đây n=1000 thì có thể chọn m=1001, số 1001 cũng là số chúng nó chọn, nhưng có thể tối ưu hơn chọn m=1024=2^10 thì phép nhân a1*m có thể tối ưu thành dịch trái 10 bit, và bước cuối cùng thay vì chia 1001 thì có thể xài dịch phải 10 bit.
C++:
struct Solution {
vector<int> shuffle(vector<int>& nums, const int n) {
for (int i = n; i--;) nums[2*i] += nums[i] << 10;
for (int i = n, j = 1; i < 2*n; ++i, j += 2) nums[j] = nums[i] & 0x3ff;
for (int i = 0; i < n; ++i) nums[2*i] >>= 10;
return move(nums);
}
};
<<10, >>10, &0x3ff chưa nhìn quen thì thấy ghê thế thoy chứ ghi là *1024, /1024, %1024 biên dịch xài
-O2
nó cũng tối ưu được hết