你当前算法的问题在于,你试图通过洗牌来摆脱死胡同——具体来说,当你的
@letters
和
@numbers
数组(在
@numbers
的初始洗牌后)产生多次相同的单元格时。这种方法在矩阵很小的情况下有效,因为不需要尝试太多次才能找到可行的重新洗牌。然而,在列表很大的情况下,这种方法是致命的。即使你可以更高效地寻找替代方案——例如,尝试排列而不是随机洗牌——这种方法也可能注定失败。
与其洗牌整个列表,不如通过对现有矩阵进行小的修改来解决问题。
例如,让我们从你的示例矩阵(称之为M1)开始。随机选择一个单元格进行更改(比如A1)。此时,矩阵处于非法状态。我们的目标是以最少的编辑次数来修复它——具体来说是3次额外的编辑。你可以通过“走动”矩阵来实现这3次额外的编辑,每次修复一行或一列都会产生另一个需要解决的问题,直到你走完整个矩形。
例如,在将A1从0更改为1后,有3种方法可以进行下一次修复:A3、B1和C1。让我们决定第一个编辑应该修复行。所以我们选择A3。在第二个编辑中,我们将修复列,所以我们有选择:B3或C3(假设是C3)。最后的修复只提供了一个选择(C1),因为我们需要返回到原始编辑的列。最终结果是一个新的有效矩阵。
Orig Change A1 Change A3 Change C3 Change C1
M1 M2
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
----- ----- ----- ----- -----
A | 0 0 1 1 0 1 1 0 0 1 0 0 1 0 0
B | 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0
C | 1 0 0 1 0 0 1 0 0 1 0 1 0 0 1
如果编辑路径导致死胡同,你需要回溯。如果所有修复路径都失败,最初的编辑可能会被拒绝。
这种方法可以快速生成新的有效矩阵。但不一定会产生随机结果:M1和M2仍然高度相关,当矩阵大小增长时,这一点将变得更加明显。
如何增加随机性?你提到大多数单元格(99%或更多)都是零。一个想法是按照以下步骤进行:对于矩阵中的每个1,将其值设置为0,然后使用上面概述的4次编辑方法来修复矩阵。实际上,你将把所有1移到新的随机位置。
这里有一个例子。可能还有进一步的速度优化,但这种方法在我的Windows电脑上,在30秒左右内以0.5%的密度产生了10个新的600x600矩阵。不知道是否足够快。
use strict;
use warnings;
main(@ARGV);
sub main {
my $n_iter = pop;
my $matrix = init_matrix(@_);
print_matrix($matrix);
for my $n (1 .. $n_iter){
warn $n, "\n";
edit_matrix($matrix);
print_matrix($matrix);
}
}
sub init_matrix {
my ($rows, $cols, $density) = @_;
my @matrix;
for my $r (1 .. $rows){
push @matrix, [ map { rand() < $density ? 1 : 0 } 1 .. $cols ];
}
return \@matrix;
}
sub print_matrix {
my $matrix = shift;
print "\n";
for my $row (@$matrix){
my @vals = map { $_ ? 1 : ''} @$row;
print join("\t", @vals), "\n";
}
}
sub edit_matrix {
my $matrix = shift;
my $move_these = cells_to_move($matrix);
for my $cell (@$move_these){
my ($i, $j) = @$cell;
$matrix->[$i][$j] = 0
if $matrix->[$i][$j]
and other_edits($matrix, $cell, 0, $j);
}
}
sub cells_to_move {
my $matrix = shift;
my $i = -1;
my @cells = ();
for my $row (@$matrix){
$i ++;
for my $j (0 .. @$row - 1){
push @cells, [$i, $j] if $matrix->[$i][$j];
}
}
return \@cells;
}
sub other_edits {
my ($matrix, $cell, $step, $last_j) = @_;
$step ++;
return 1 if $step > 3;
my ($i, $j) = @$cell;
my @fixes;
if ($step == 1){
@fixes =
map { [$i, $_] }
grep { $_ != $j and not $matrix->[$i][$_] }
0 .. @{$matrix->[0]} - 1
;
shuffle(\@fixes);
}
elsif ($step == 2) {
@fixes =
map { [$_, $j] }
grep { $_ != $i and $matrix->[$_][$j] }
0 .. @$matrix - 1
;
shuffle(\@fixes);
}
else {
@fixes = ([$i, $last_j]) unless $matrix->[$i][$last_j];
}
for my $f (@fixes){
if ( other_edits($matrix, [@$f], $step, $last_j) ){
$matrix->[$f->[0]][$f->[1]] = $step == 2 ? 0 : 1;
return 1;
}
}
return;
}
sub shuffle {
my $array = shift;
my $i = scalar(@$array);
my $j;
for (@$array ){
$i --;
$j = int rand($i + 1);
@$array[$i, $j] = @$array[$j, $i] unless $i == $j;
}
}
shuffle
的链接,只是用一个指向某个网页托管公司的404
链接替换了它。我会说你在这里继续发送垃圾信息。 - Sinan Ünür