如果我们假设数字范围将始终为2 ^ n(2的偶数次幂),则“异或”将起作用(如另一幅海报所示)。至于为什么,让我们证明一下:
理论
给定任何从零开始的整数范围,其中有2^n
一个元素丢失一个元素,您可以通过简单地将已知值异或在一起以得出丢失的数字来找到该丢失的元素。
证据
让我们看一下n =2。对于n = 2,我们可以表示4个唯一整数:0、1、2、3。它们的位模式为:
现在,如果我们看的话,每一位都精确地设置了两次。因此,由于将其设置为偶数,并且数字的“异或”运算将产生0。如果缺少单个数字,则“异或”将产生一个数字,当与缺失的数字进行异或运算时,将导致0。因此,丢失的数字和所得的异或数字完全相同。如果我们删除2,则结果xor将为10
(或2)。
现在,让我们看一下n + 1。让我们调用设置每个位的次数n
,x
以及设置每个位的次数n+1
y
。的值y
等于,y = x * 2
因为有些x
元素的n+1
位设置为0,而x
元素的n+1
位设置为1。由于2x
将始终为偶数,n+1
因此始终将每个位设置为偶数次。
因此,由于n=2
作品,n+1
作品,异或方法进行的所有值工作n>=2
。
基于0的范围的算法
这很简单。它使用2 * n位内存,因此对于<= 32的任何范围,将使用2个32位整数(忽略文件描述符消耗的任何内存)。它使文件一次通过。
long supplied = 0;
long result = 0;
while (supplied = read_int_from_file()) {
result = result ^ supplied;
}
return result;
基于任意距离的算法
只要总范围等于2 ^ n,该算法就适用于任何起始数字到任何终止数字的范围...这基本上是重新确定范围的最小范围为0。但是它确实需要2次通过通过文件(第一个获取最小值,第二个计算缺失的int)。
long supplied = 0;
long result = 0;
long offset = INT_MAX;
while (supplied = read_int_from_file()) {
if (supplied < offset) {
offset = supplied;
}
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
result = result ^ (supplied - offset);
}
return result + offset;
任意范围
我们可以将此修改后的方法应用于一组任意范围,因为所有范围都将至少一次穿越2 ^ n的幂。仅在缺少单个位时有效。它需要2次传递未排序的文件,但是每次都会找到一个丢失的数字:
long supplied = 0;
long result = 0;
long offset = INT_MAX;
long n = 0;
double temp;
while (supplied = read_int_from_file()) {
if (supplied < offset) {
offset = supplied;
}
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
n++;
result = result ^ (supplied - offset);
}
// We need to increment n one value so that we take care of the missing
// int value
n++
while (n == 1 || 0 != (n & (n - 1))) {
result = result ^ (n++);
}
return result + offset;
基本上,将范围重新设定为0左右。然后,在计算异或时,它会计算要附加的未排序值的数量。然后,它将1加到未排序值的计数上以处理缺失值(计算缺失值)。然后,继续对n值进行异或运算,每次将其递增1,直到n为2的幂为止。然后将结果重新基于原始值。做完了
这是我在PHP中测试的算法(使用数组而不是文件,但概念相同):
function find($array) {
$offset = min($array);
$n = 0;
$result = 0;
foreach ($array as $value) {
$result = $result ^ ($value - $offset);
$n++;
}
$n++; // This takes care of the missing value
while ($n == 1 || 0 != ($n & ($n - 1))) {
$result = $result ^ ($n++);
}
return $result + $offset;
}
以任意范围的值(我测试过包括否定值)将数组放入其中,而该范围内缺少一个值,则每次都会找到正确的值。
另一种方法
由于我们可以使用外部排序,所以为什么不检查间隙呢?如果我们假设文件在运行此算法之前已排序:
long supplied = 0;
long last = read_int_from_file();
while (supplied = read_int_from_file()) {
if (supplied != last + 1) {
return last + 1;
}
last = supplied;
}
// The range is contiguous, so what do we do here? Let's return last + 1:
return last + 1;