Если вам нужно что-то более быстрое, вы можете написать собственный генератор последовательности.
gather {
loop (my int $i = 1; $i < 1_000_000; $i += 2) {
take $i
}
}
.grep( -> $x { $x eq $x.flip } )
.grep( -> $y { $y.base(2) eq $y.base(2).flip } )
.sum.say
Это занимает около 4 секунд.
Или go еще быстрее , вы можете создать объект Iterator самостоятельно.
class Odd does Iterator {
has uint $!count = 1;
method pull-one () {
if ($!count += 2) < 1_000_000 {
$!count
} else {
IterationEnd
}
}
}
Seq.new(Odd.new)
.grep( -> $x { $x == $x.flip } )
.grep( -> $y { $y.base(2) == $y.base(2).flip } )
.sum.say
Это занимает всего около 2 секунд.
Конечно, если вы хотите go как можно быстрее, избавьтесь итерации последовательности полностью.
Также используйте нативный int
s.
Также кэшируйте строку из 10 строк. (my $s = ~$x)
my int $acc = 0;
loop ( my int $x = 1; $x < 1_000_000; $x += 2) {
next unless (my $s = ~$x) eq $s.flip;
next unless $x.base(2) eq $x.base(2).flip;
$acc += $x
}
say $acc;
Что снижает его до примерно 0.45
секунд.
(Кэширование .base(2)
, похоже, ничего не делает.)
Это, вероятно, близко к минимуму, не прибегая к непосредственному использованию nqp
ops.
Я попытался написать собственный битный флиппер int, но он замедлился. 0.5
секунд.
(Я не придумал этот алгоритм, я только адаптировал его к Raku. Я также добавил +> $in.msb
, чтобы решить эту проблему.)
Я бы предположил, что spe sh уходит в операции, которые не должны быть там.
Или, может быть, это не JIT очень хорошо.
Это может быть больше производительности для значений, больших 1_000_000
.
(.base(2).flip
равно O(log n)
, тогда как O(1)
.)
sub flip-bits ( int $in --> int ) {
my int $n =
((($in +& (my int $ = 0xaaaaaaaa)) +> 1) +| (($in +& (my int $ = 0x55555555)) +< 1));
$n = ((($n +& (my int $ = 0xcccccccc)) +> 2) +| (($n +& (my int $ = 0x33333333)) +< 2));
$n = ((($n +& (my int $ = 0xf0f0f0f0)) +> 4) +| (($n +& (my int $ = 0x0f0f0f0f)) +< 4));
$n = ((($n +& (my int $ = 0xff00ff00)) +> 8) +| (($n +& (my int $ = 0x00ff00ff)) +< 8));
((($n +> 16) +| ($n+< 16)) +> (32 - 1 - $in.msb)) +& (my int $ = 0xffffffff);
}
…
# next unless (my $s = ~$x) eq $s.flip;
next unless $x == flip-bits($x);
Можно даже попробовать использовать несколько потоков.
Обратите внимание, что эта рабочая нагрузка слишком мала для того, чтобы она была эффективной.
Затраты на использование потоков сводят на нет любую выгоду.
my atomicint $total = 0;
sub process ( int $s, int $e ) {
# these are so the block lambda works properly
# (works around what I think is a bug)
my int $ = $s;
my int $ = $e;
start {
my int $acc = 0;
loop ( my int $x = $s; $x < $e; $x += 2) {
next unless (my $s = ~$x) eq $s.flip;
next unless $x.base(2) eq $x.base(2).flip;
$acc += $x;
}
$total ⚛+= $acc;
}
}
my int $cores = (Kernel.cpu-cores * 2.2).Int;
my int $per = 1_000_000 div $cores;
++$per if $per * $cores < 1_000_000;
my @promises;
my int $start = 1;
for ^$cores {
my int $end = $start + $per - 2;
$end = 1_000_000 if $end > 1_000_000;
push @promises, process $start, $end;
#say $start, "\t", $end;
$start = $end + 2;
}
await @promises;
say $total;
, которая выполняется примерно за 0.63
секунд .
(Я запутался со значением 2.2
, чтобы найти почти минимальное время на моем компьютере.)