我有以下稀疏矩阵A.
2 3 0 0 0
3 0 4 0 6
0 -1 -3 2 0
0 0 1 0 0
0 4 2 0 1
然后我想从那里获取以下信息:
条目的累积计数,因为矩阵是按列扫描的。 产量:
Ap = [0,2,5,9,10,12];
条目的行索引,因为矩阵是按列扫描的。 产量:
Ai = [0,1,0,2,4,1,2,3,4,2,1,4];
非零矩阵条目,因为矩阵是按列扫描的。 产量:
Ax = [2,3,3,-1,4,4,-3,1,2,2,6,1];
由于实际矩阵A可能非常大,是否有任何有效的方法 在Perl中可以捕获这些元素?特别是没有啜饮所有基质A. 进入RAM。
我坚持使用以下代码。哪不能给我想要的东西。
use strict;
use warnings;
my (@Ax, @Ai, @Ap) = ();
while (<>) {
chomp;
my @elements = split /\s+/;
my $i = 0;
my $new_line = 1;
while (defined(my $element = shift @elements)) {
$i++;
if ($element) {
push @Ax, 0 + $element;
if ($new_line) {
push @Ai, scalar @Ax;
$new_line = 0;
}
push @Ap, $i;
}
}
}
push @Ai, 1 + @Ax;
print('@Ax = [', join(" ", @Ax), "]\n");
print('@Ai = [', join(" ", @Ai), "]\n");
print('@Ap = [', join(" ", @Ap), "]\n");
答案 0 :(得分:1)
这就是你要找的东西,我想:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper::Simple;
my @matrix;
# Populate @matrix
while (<>) {
push @matrix, [ split /\s+/ ];
}
my $columns = @{ $matrix[0] };
my $rows = @matrix;
my ( @Ap, @Ai, @Ax );
my $ap = 0;
for ( my $j = 0 ; $j <= $rows ; $j++ ) {
for ( my $i = 0 ; $i <= $columns ; $i++ ) {
if ( $matrix[$i]->[$j] ) {
$ap++;
push @Ai, $i;
push @Ax, $matrix[$i]->[$j];
}
}
push @Ap, $ap;
}
print Dumper @Ap;
print Dumper @Ai;
print Dumper @Ax;
答案 1 :(得分:1)
存储稀疏数据的常用策略是删除您不关心的值(零)并使用您关注的每个值存储行索引和列索引,从而保留其位置信息:
[VALUE, ROW, COLUMN]
在您的情况下,您可以进一步节省,因为可以通过逐列处理数据来满足您的所有需求,这意味着我们不必为每个值重复COLUMN。
use strict;
use warnings;
use Data::Dumper;
my ($r, $c, @dataC, @Ap, @Ai, @Ax, $cumul);
# Read data row by row, storing non-zero values by column.
# $dataC[COLUMN] = [
# [VALUE, ROW],
# [VALUE, ROW],
# etc.
# ]
$r = -1;
while (<DATA>) {
chomp;
$r ++;
$c = -1;
for my $v ( split '\s+', $_ ){
$c ++;
push @{$dataC[$c]}, [$v, $r] if $v;
}
}
# Iterate through the data column by column
# to compute the three result arrays.
$cumul = 0;
@Ap = ($cumul);
$c = -1;
for my $column (@dataC){
$c ++;
$cumul += @$column;
push @Ap, $cumul;
for my $value (@$column){
push @Ax, $value->[0];
push @Ai, $value->[1];
}
}
__DATA__
2 3 0 0 0
3 0 4 0 6
0 -1 -3 2 0
0 0 1 0 0
0 4 2 0 1
答案 2 :(得分:1)
更新了。如果您不想存储任何原始数据:
#!/usr/bin/perl
use strict;
use warnings;
my %matrix_info;
while ( <DATA> ) {
chomp;
last unless /[0-9]/;
my @v = map {0 + $_ } split;
for (my $i = 0; $i < @v; ++$i) {
if ( $v[$i] ) {
push @{ $matrix_info{$i}->{indices} }, $. - 1;
push @{ $matrix_info{$i}->{nonzero} }, $v[$i];
}
}
}
my @cum_count = (0);
my @row_indices;
my @nonzero;
for my $i ( sort {$a <=> $b } keys %matrix_info ) {
my $mi = $matrix_info{$i};
push @nonzero, @{ $mi->{nonzero} };
my @i = @{ $mi->{indices} };
push @cum_count, $cum_count[-1] + @i;
push @row_indices, @i;
}
print(
"\@Ap = [@cum_count]\n",
"\@Ai = [@row_indices]\n",
"\@Ax = [@nonzero]\n",
);
__DATA__
2 3 0 0 0
3 0 4 0 6
0 -1 -3 2 0
0 0 1 0 0
0 4 2 0 1
<强>输出:强>
C:\Temp> m @Ap = [0 2 5 9 10 12] @Ai = [0 1 0 2 4 1 2 3 4 2 1 4] @Ax = [2 3 3 -1 4 4 -3 1 2 2 6 1]
答案 3 :(得分:0)
Ap很简单:只需从零开始并在每次遇到非零数字时递增。我没有看到你试图在@Ap中写任何东西,所以毫不奇怪它不会像你希望的那样结束。
艾和斧比较棘手:你想在行扫描时进行逐列排序。您将无法就地执行任何操作,因为您还不知道列将产生多少元素,因此您无法事先知道元素的位置。
显然,如果您可以改变要求进行行级排序,那将会更容易。如果做不到这一点,你可能会变得复杂并收集(i,j,x)三元组。在收集时,它们自然会被(i,j)命令。收集后,您只想按(j,i)对它们进行排序。
答案 4 :(得分:0)
您提供的代码逐行运行。要按列顺序获取结果,您必须将值累积到单独的数组中,每列一个:
# will look like ([], [], [] ...), one [] for each column.
my @columns;
while (<MATRIX>) {
my @row = split qr'\s+';
for (my $col = 0; $col < @row; $col++) {
# push each non-zero value into its column
push @{$columns[$col]}, $row[$col] if $row[$col] > 0;
}
}
# now you only need to flatten it to get the desired kind of output:
use List::Flatten;
@non_zero = flat @columns;