Как получить равномерно распределенный образец из значений массива Perl?
У меня есть массив, содержащий много значений от 0 до 360 (например, градусы по кругу), но распределен неравномерно:
1,45,46,47,48,49,50,51,52,53,54,55,100,120,140,188, 210, 280, 355
Теперь мне нужно уменьшить эти значения, например, только до 4, но как можно более равномерно распределенные значения.
Как это сделать?
Спасибо Ян
2 ответа
Положите цифры по кругу, как часы. Теперь создайте логический крест, скажем, в 12, 3, 6 и 9 часов. Поставьте 12 на первое число. Теперь найдите, какие числа будут ближайшими к 3, 6 и 9 часам, и запишите сумму расстояний этих трех чисел рядом с первым числом.
Повторяйте, вращая верхнюю часть вашего креста - точку 12 часов - по часовой стрелке, пока она точно не совпадет со следующим числом. Снова измерьте расстояние до ближайших чисел к каждой из трех других точек пересечения и запишите этот результат рядом с текущим 12-часовым номером.
Повторяйте, пока не достигнете того, что ваш 12-часовой поворот повернут полностью до первоначального 3-го часа, после чего вы закончите. Какой бы номер не был назначен наименьшей сумме, он определяет конфигурацию выигрыша.
Это решение обобщает любой диапазон значений R и любое число N конечных точек, к которым вы хотите уменьшить набор. Каждая точка на "кресте" находится на расстоянии R/N друг от друга, и вам нужно только вращать, пока вершина вашего креста не достигнет точки, где следующий рычаг находился в исходном положении. Таким образом, если вы хотите получить 6 точек, у вас будет 6-точечный крест, каждый с шагом 60 градусов, а не 4-точечный крест с углом 90 градусов. Если ваш диапазон отличается, вы все равно выполняете ту же операцию. Таким образом, вам не нужны физические часы и кросс для реализации этого алгоритма: он работает для любых R и N.
Мне не нравится этот ответ с точки зрения Perl, так как мне не удалось включить в решение какие-либо знаки доллара.:)
Используйте алгоритм кластеризации, чтобы разделить ваши данные на равномерно распределенные разделы. Затем возьмите случайное значение из каждого кластера. Следующие $datafile
выглядит так:
1 1
45 45
46 46
...
210 210
280 280
355 355
Первый столбец - это тег, второй столбец - данные. Выполнение следующего с $K = 4
:
use strict; use warnings;
use Algorithm::KMeans;
my $datafile = $ARGV[0] or die;
my $K = $ARGV[1] or 0;
my $mask = 'N1';
my $clusterer = Algorithm::KMeans->new(
datafile => $datafile,
mask => $mask,
K => $K,
terminal_output => 0,
);
$clusterer->read_data_from_file();
my ($clusters, $cluster_centers) = $clusterer->kmeans();
my %clusters;
while (@$clusters) {
my $cluster = shift @$clusters;
my $center = shift @$cluster_centers;
$clusters{"@$center"} = $cluster->[int rand( @$cluster - 1)];
}
use YAML; print Dump \%clusters;
возвращает это:
120: 120
199: 188
317.5: 355
45.9166666666667: 46
Первый столбец - центр кластера, второй - выбранное значение из этого кластера. Расстояние между центрами должно быть максимизировано в соответствии с алгоритмом максимизации ожидания.