它不适用于负数,但速度很快。
实际上,如果你想对负数进行异或运算,你只需要将它们字符串化:
my @array = (-10..-7,-5..10,-10..10);
my $unique;
$unique ^= "$_" for @array;
say $unique;
输出
-6
并做一些快速的基准测试:
Benchmark: timing 100 iterations of schwern, there_can_be_only_one, tobyink, xor_string...
schwern: 323 wallclock secs (312.42 usr + 7.08 sys = 319.51 CPU) @ 0.31/s (n=100)
there_can_be_only_one: 114 wallclock secs (113.49 usr + 0.02 sys = 113.51 CPU) @ 0.88/s (n=100)
tobyink: 177 wallclock secs (176.76 usr + 0.14 sys = 176.90 CPU) @ 0.57/s (n=100)
xor_string: 98 wallclock secs (97.05 usr + 0.00 sys = 97.05 CPU) @ 1.03/s (n=100)
表明对字符串进行异或运算比将数学转换为正数进行异或运算快 15%。
推论 - 排序列表怎么样?
Schwern 的解决方案提出了一个有趣的推论。他对列表进行了排序,然后搜索了所有独特的元素。
如果我们使用额外信息,即在一堆双胞胎中只有一个单胞胎,我们可以通过成对比较来快速简化搜索,从而将我们的比较减少 4 倍。
但是,我们可以通过二分搜索做得更好。如果我们在已知匹配对之间的障碍上分隔列表,那么剩下的两个列表中的任何一个奇数都包含我们的单例。我对这个解决方案做了一些基准测试,它比其他任何东西都要快几个数量级(当然):
use strict;
use warnings;
use Benchmark qw(timethese);
sub binary_search {
my $nums = $_[0];
my $min = 0;
my $max = $#$nums;
while ($min < $max) {
my $half = ($max - $min) / 2; # should always be an integer
my ($prev, $this, $next) = ($min+$half-1) .. ($min+$half+1);
if ($nums->[$prev] == $nums->[$this]) {
if ($half % 2) { # 0 0 1 1 2 2 3 ( half = 3 )
$min = $next;
} else { # 0 1 1 2 2 ( half = 2 )
$max = $prev - 1;
}
} elsif ($nums->[$this] == $nums->[$next]) {
if ($half % 2) { # 0 1 1 2 2 3 3 ( half = 3 )
$max = $prev;
} else { # 0 0 1 1 2 ( half = 2 )
$min = $next + 1;
}
} else {
$max = $min = $this;
}
}
return $nums->[$min];
}
sub xor_string {
my $tmp;
$tmp ^= "$_" for @{$_[0]};
}
sub brute {
my $nums = $_[0];
return $nums->[0] if $nums->[0] != $nums->[1];
for (1..$#$nums-1) {
my($prev, $this, $next) = @$nums[$_-1, $_, $_+1];
return $this if $prev != $this && $next != $this;
}
return $nums->[-1] if $nums->[-1] != $nums->[-2];
}
sub pairwise_search {
my $nums = $_[0];
for (my $i = 0; $i <= $#$nums; $i += 2) {
if ($nums->[$i] != $nums->[$i+1]) {
return $nums->[$i];
}
}
}
# Note: this test data is very specific and is intended to take near the maximum
# number of steps for a binary search while shortcutting halfway for brute force
# and pairwise
my @input = sort {$a <=> $b} (0..500_003, 500_005..1_000_000, 0..1_000_000);
#my @input = sort {$a <=> $b} (0..499_996, 499_998..1_000_000, 0..1_000_000);
timethese(1000, {
brute => sub { brute(\@input) },
pairwise => sub { pairwise_search(\@input) },
xor_string => sub { xor_string(\@input) },
binary => sub { binary_search(\@input) },
});
结果:
Benchmark: timing 1000 iterations of binary, brute, pairwise, xor_string...
binary: 0 wallclock secs ( 0.02 usr + 0.00 sys = 0.02 CPU) @ 62500.00/s (n=1000)
(warning: too few iterations for a reliable count)
brute: 472 wallclock secs (469.92 usr + 0.05 sys = 469.97 CPU) @ 2.13/s (n=1000)
pairwise: 216 wallclock secs (214.74 usr + 0.00 sys = 214.74 CPU) @ 4.66/s (n=1000)
xor_string: 223 wallclock secs (221.74 usr + 0.06 sys = 221.80 CPU) @ 4.51/s (n=1000)