Perl: ранжировать, но не сортировать массив 1D
Мне нужен код Perl для ранжирования чисел в массиве в другой массив без сортировки. Итак, input: (10, 4, 2, 9, 32) => output: (4, 2, 1, 3, 5)
У меня есть этот код, который близок, но я считаю его бесполезным, потому что ввод и вывод не совсем то, что я хочу:
use strict;
use warnings;
use Data::Dumper;
my %data =
(
1 => 10,
2 => 4,
3 => 2,
4 => 9,
5 => 32,
);
my ($n, @rank) = 1;
foreach( keys %data){
$rank[ $data{$_} ] .= "$_ ";
}
defined and $n += print for @rank;
Вышеприведенный код выводит:
3 2 4 1 5
Вывод неверный, а вывод вообще не обязателен, я просто хочу, чтобы мой массив результатов @rank был как одномерный массив, как описано. Желательно, если это возможно без назначения ключей для входных данных массива.
2 ответа
Это можно сделать в два этапа: сначала отсортировать индексы по массиву, затем отсортировать индексы по этим индексам, чтобы получить ранг (на основе 0), и добавить один, чтобы получить ранг на основе 1.
my @array = (10, 4, 2, 9, 32);
my @index_by_rank_minus_1 = sort { $array[$a] <=> $array[$b] } 0..$#array;
my @ranks =
map $_+1,
sort { $index_by_rank_minus_1[$a] <=> $index_by_rank_minus_1[$b] } 0..$#array;
Кроме того, следующее может быть немного быстрее (хотя это, вероятно, имеет значение только для очень длинных списков).
my @array = (10, 4, 2, 9, 32);
my %rank_by_index;
@rank_by_index{ sort { $array[$a] <=> $array[$b] } 0..$#array } = 1..@array;
my @ranks = map { $rank_by_index{$_} } 0..$#array;
Вот мое решение:
use strict;
use warnings;
my @numbers = (10, 4, 2, 9, 32);
my %rank_of;
@rank_of{sort { $a <=> $b } @numbers} = 1 .. @numbers;
print join(" ", map $rank_of{$_}, @numbers), "\n";
Мы создаем хеш, который сопоставляет каждое число с соответствующим ему рангом, а затем печатаем ранг каждого числа в порядке исходного массива.
Мы вычисляем ранг путем объединения каждого числа (в отсортированном порядке) со списком 1, 2, 3, ..., то есть:
2 4 9 10 32 # sort { $a <=> $b } @numbers
| | | | |
1 2 3 4 5 # 1 .. @numbers