5

我有两组整数AB(大小A小于或等于B),我想回答这个问题,“离 有多近AB”。我想回答这个问题的方法是衡量你必须从一个给定a的 in走多远A才能找到一个bin B

我想产生的具体措施如下:对于每个a,找到最接近b的,唯一的问题是一旦我将 ab与 an匹配a,我就不能再用它b来匹配任何其他a的了。(编辑:我试图实现的算法总是更喜欢较短的匹配。因此,如果b最近的邻居不止一个a,请选择a最接近的b。如果不止一个a具有相同的距离,我不确定该怎么办to b, 现在我正在选择a前面的b,但这是非常随意的,不一定是最佳的。)我将用于制作这些集合(最终产品)的度量是一个直方图,显示垂直轴上的对数和 x 轴上对的距离。

因此,如果A = {1, 3, 4}B = {1, 5, 6, 7},我将得到以下a,b对:1,1, 4,5, 3,6. 对于这些数据,直方图应显示距离为零的一对、距离为 1 的一对和距离为 3 的一对。

(这些集合的实际大小有大约 100,000 个元素的上限,我从已经从低到高排序的磁盘中读取它们。整数范围从 1 到 ~20,000,000。编辑:另外, 和 的元素AB唯一的,即没有重复元素。)


我想出的解决方案感觉有点笨拙。我正在使用 Perl,但问题或多或少与语言无关。

  1. 首先我做一个散列,对于出现在 和 的联合中的每个数字都有一个键,A并且B值指示每个数字是否出现在AB或两者中,例如,$hash{5} = {a=>1, b=>1}如果数字 5 出现在两个数据集中。(如果它只出现在 中A,你就会有$hash{5} = {a=>1}。)

  2. 接下来,我迭代A查找出现在Aand中的所有哈希元素,B在度量中标记它们,然后将它们从哈希中删除。

  3. 然后,我对所有散列键进行排序,并使散列的每个元素指向其最近的邻居,就像一个链表,其中给定的散列元素现在看起来像$hash{6} = {b=>1, previous=>4, next=>8}. 链表不知道下一个和上一个元素是否在A或中B

  4. 然后我循环从 开始的对距离d=1,并找到所有具有距离的对d,标记它们,将它们从散列中删除,直到没有更多的元素A匹配。

循环如下所示:

for ($d=1; @a > 0; $d++) {
    @left = ();
    foreach $a in @a {
        $next = $a;
        # find closest b ahead of $a, stop searching if you pass $d
        while (exists $hash{$next}{next} && $next - $a < $d) {
            $next = $hash{$next}{next};
        }
        if ($next is in B && $next - $a == $d) {
            # found a pair at distance $d
            mark_in_measure($a, $next);
            remove_from_linked_list($next);
            remove_from_linked_list($a);
            next;
        }

        # do same thing looking behind $a
        $prev = $a;
        ...

        # you didn't find a match for $a
        push @left, $a;
    }
    @a = @left;
}

这个循环显然更喜欢匹配b's 出现晚于a's; 的对。我不知道是否有一种明智的方法来决定以后是否比以前更好(在获得更接近的配对方面更好)。我感兴趣的主要优化是处理时间。

4

2 回答 2

2

听起来您有分配问题的特殊情况(在加权二分图中找到最小匹配)。

解决分配问题的算法在 O(N^3) 对你来说太慢了,但我很确定你可以通过利用你的特定权重函数或者你只想要一个直方图而不是精确匹配。

于 2011-10-12T16:59:13.210 回答
1
#!/usr/bin/perl

use strict;
use warnings FATAL => 'all';
use diagnostics;  

# http://www.hungarianalgorithm.com/solve.php?c=3-2-6-22--7-2-2-18--13-8-4-12--23-18-14-2&random=1
# https://www.topcoder.com/community/data-science/data-science-tutorials/assignment-problem-and-hungarian-algorithm/
# http://www.cse.ust.hk/~golin/COMP572/Notes/Matching.pdf

my @mat;
my @out_mat;

my $spaces    = 6;
my $precision = 0;

my $N = 10;
my $M = 12;
my $r = 100;

my @array1; my @array2;

for my $i (1..$N) {
    push @array1, sprintf( "%.${precision}f",  rand($r)  );
}

for my $i (1..$M) {
    push @array2, sprintf( "%.${precision}f",  rand($r)  );
}

#@array1 = ( 1, 3, 4);      # $mat[i]->[j] = abs( array1[i] - array2[j] )
#@array2 = ( 1, 5, 6, 7);

#                1     5     6     7

#     1     [    0*    4     5     6 ]

#     3     [    2     2*    3     4 ]

#     4     [    3     1     2*    3 ]

my $min_size  = $#array1 < $#array2 ? $#array1 : $#array2;
my $max_size  = $#array1 > $#array2 ? $#array1 : $#array2;

for (my $i = 0; $i < @array1; $i++){
   my @weight_function;
   for (my $j = 0; $j < @array2; $j++){
      my $dif = sprintf( "%.${precision}f", abs ($array1[$i] - $array2[$j])  );
      #my $dif = sprintf( "%.${precision}f", ($array1[$i] - $array2[$j])**2  ); 
      push @weight_function, $dif;
   }
   push @mat, \@weight_function;
}


# http://cpansearch.perl.org/src/TPEDERSE/Algorithm-Munkres-0.08/lib/Algorithm/Munkres.pm

Algorithm::Munkres::assign(\@mat,\@out_mat);


print "\n\@out_mat index  = [";
for my $index (@out_mat) {
   printf("%${spaces}d", $index);
}
print " ]\n";

print "\@out_mat values = [";

my %hash;
for my $i (0 .. $max_size){
   my $j = $out_mat[$i];
   last if ( $i > $min_size and $#array1 < $#array2 );
   next if ( $j > $min_size and $#array1 > $#array2 );
   my $dif = $mat[$i]->[$j];
   printf( "%${spaces}.${precision}f", $dif );
   $hash{ $dif } { $i } { 'index_array1' } = $i;
   $hash{ $dif } { $i } { 'index_array2' } = $j;
   $hash{ $dif } { $i } { 'value_array1' } = $array1[$i];
   $hash{ $dif } { $i } { 'value_array2' } = $array2[$j]; 
}

print " ]\n\n";


my $soma_da_dif = 0;

foreach my $min_diferenca ( sort { $a <=> $b } keys %hash ){
   foreach my $k ( sort { $a <=> $b } keys %{$hash{$min_diferenca}} ){
      $soma_da_dif += $min_diferenca;
      my $index_array1 = $hash{ $min_diferenca } { $k } { 'index_array1' };
      my $index_array2 = $hash{ $min_diferenca } { $k } { 'index_array2' };
      my $value_array1 = $hash{ $min_diferenca } { $k } { 'value_array1' };
      my $value_array2 = $hash{ $min_diferenca } { $k } { 'value_array2' };
      printf( "   index (%${spaces}.0f,%${spaces}.0f), values (%${spaces}.${precision}f,%${spaces}.${precision}f), dif = %${spaces}.${precision}f\n", 
              $index_array1, $index_array2, $value_array1, $value_array2, $min_diferenca );

   }
}
print "\n\nSum = $soma_da_dif\n";





#-------------------------------------------------#
#------------------ New-Package ------------------# 

{ # start scope block

package Algorithm::Munkres;

use 5.006;
use strict;
use warnings;

require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw( assign );
our $VERSION = '0.08';

...
... <---- copy all the 'package Algorithm::Munkres' here
...

return $minval;
}

1;  # don't forget to return a true value from the file

} # end scope block
于 2016-10-14T02:47:06.123 回答