在此脚本中
#!/usr/bin/perl -w
use strict;
my @ar = (1,2,10,3,5);
@ar = sort {$a <=> $b} @ar;
@ar
现在包含(1,2,3,5,10)
。
现在我想获得逆数组(4,6,7,8,9)
。
有什么建议可以做到吗?
答案 0 :(得分:8)
使用set操作时,哈希工作正常:
my %have = map {$_ => 1} @ar;
my @inv = grep {not $have{$_}} 1 .. 10;
print "@inv\n"; # 4 6 7 8 9
如果您事先不知道边界,并希望从@ar
的最小值/最大值确定它们,因为它的排序变得容易:
my @inv = grep {not $have{$_}} $ar[0] .. $ar[-1];
答案 1 :(得分:6)
这是智能匹配的一个很好的应用程序:
@inverse = grep { ! ($_ ~~ @ar) } 1..10;
选择1到10之间不在@ar
中的所有值。
答案 2 :(得分:4)
use warnings;
use strict;
use Acme::Tools qw(minus);
my @ar = (1,2,10,3,5);
@ar = sort {$a <=> $b} @ar;
my @all = 1..10;
my @inv = minus( \@all, \@ar );
print "@ar\n";
print "@inv\n";
答案 3 :(得分:3)
由于您知道数组已排序,因此在任何给定时间只需要比较1个值,因此您可以这样做:
my @ar = (1,2,10,3,5);
@ar = sort {$a <=> $b} @ar;
my @inverse = do {
my $i = 0;
grep { $_ != $ar[$i] or (++$i, 0) } 1 .. $ar[-1]
};
如此处所写,您不需要检查数组末尾的$i
,因为范围在$ar[-1]
结束。如果你改变了这个条件,那么你需要检查$i > $#ar
,或者在计算逆转之前将N + 1推到@ar
并在之后将其弹出(其中N是范围的最大值) )。此代码还假定数组中没有任何重复值。
我决定使用5,000到10,000之间的5000个数字对主要候选人进行基准测试:
use 5.010;
use Benchmark 'cmpthese';
my (@orig, %used);
while (@orig < 5000) {
my $rand = 1 + int rand 10000;
push @orig, $rand unless $used{$rand}++;
}
my @ar = sort {$a <=> $b} @orig;
cmpthese(-3, {
sorted => sub {
push @ar, 10001;
my @inverse = do {
my $i = 0;
grep { $_ != $ar[$i] or (++$i, 0) } 1 .. 10000
};
pop @ar;
},
unsorted => sub {
@ar = sort {$a <=> $b} @orig;
push @ar, 10001;
my @inverse = do {
my $i = 0;
grep { $_ != $ar[$i] or (++$i, 0) } 1 .. 10000
};
pop @ar;
},
hash => sub {
my %have = map {$_ => 1} @ar;
my @inverse = grep {not $have{$_}} 1 .. 10000;
},
smartmatch => sub {
my @inverse = grep { ! ($_ ~~ @ar) } 1 .. 10000;
},
});
在Perl 5.10.1上,我得到了:
Rate smartmatch hash unsorted sorted
smartmatch 0.708/s -- -100% -100% -100%
hash 180/s 25279% -- -7% -67%
unsorted 193/s 27183% 8% -- -65%
sorted 551/s 77745% 207% 185% --
如您所见,对阵列的反复智能匹配是慢。如果您包括排序@ar
所需的时间,我的方法与基于哈希的方法的速度大致相同。如果你打折(也许你出于其他原因必须排序@ar
),那么我的方法大约是哈希的两倍。
答案 4 :(得分:0)
我的Perl有点生疏,但这是基本的想法。
# Put the contents of `@ar` into the keys
# of a temporary hash variable (to make lookups easy)
my %temp_hash = %{{ map { $_ => 1 } @ar }};
# Then use a `for` loop starting at the first value
# in `@ar` and ending at the last value:
for (my $i = $ar[0]; $i <= $ar[$#ar-1]; $i++) {
# For each value of your loop counter,
# check whether that value exists as a key
# in your temporary hash variable.
# If it doesn't, push it on to your inverse array:
push @inverse_array, $i if (not exists $temp_hash{$i});
}
完成工作!
答案 5 :(得分:0)
这是使用Set :: IntSpan的解决方案。
#!/usr/bin/perl
use strict;
use warnings;
use Set::IntSpan;
my @ar = (1,2,10,3,5);
@ar = sort {$a <=> $b} @ar;
# min - max of given set
my $range = Set::IntSpan->new( "$ar[0]-$ar[-1]" );
# members of set
my $used = Set::IntSpan->new( @ar );
# Calculates the missing numbers
my $unused = $range->diff( $used );
print join " ", $unused->elements;
答案 6 :(得分:-1)
简单
@inv = sort {$b <=> $a} @ar;
如果要反转随机非排序数组:
my @inv;
while(scalar @ar){
push @inv pop @ar;
}