我有一个字符串例如
1234567890
我想将该字符串的随机位置替换为另一组其他字符串中的随机序列中的相应位置,例如
ABCDEFGHIJ
KLMNOPQRST
UVWXYZABCD
...
如果我选择进行 3 个替换,则脚本应选择 3 个随机数,例如 3,7,8;以及3个随机序列,例如1、1、3;进行替换以生成预期的输出:
12C456GB90
有没有办法在没有明显循环的情况下做到这一点?我编写了一个简单的 bash 脚本来生成随机位置和随机序列行,然后进行 1 次替换,然后在输出上重复该过程,重复,重复。这工作得很好,但是在我的现实生活文件(比示例大得多)中,我想生成 10,000 个或更多替换。哦,我需要多次执行此操作才能生成多个“突变”变体序列。
编辑:目前我正在使用这样的东西:
#chose random number between 1 and the number of characters in the string
randomposition=$(jot -r 1 1 $seqpositions)
#chose a random number between 1 and the number of lines in the set of potential replacement strings
randomline=$(jot -r 1 1 $alignlines)
#find the character at randomline:randomposition
newAA=$(sed -n "$randomline,$randomline p" $alignmentfile | cut -c$randomposition)
#replace the character at 'string:randomposition' with the character at 'randomline:randomposition'
sed "s/./$newAA/$randomposition" $sequencefile
(显然还有一些额外的位)并且只是循环数千次
答案1
笔记:
这纯粹是出于娱乐目的;中的等效程序C
会简单得多并且速度快几个数量级;至于bash
,我们甚至不谈论;-)
以下perl
脚本将在我的笔记本电脑上在大约 10 秒内突变约 1M 序列和约 10k 比对的列表。
#! /usr/bin/perl
# usage mutagen number_of_replacements alignment_file [ sequence_file ..]
use strict;
my $max = shift() - 1;
my $algf = shift;
open my $alg, $algf or die "open $algf: $!";
my @alg = <$alg>;
sub prand { map int(rand() * $_[0]), 0..$max }
while(<>){
my @ip = prand length() - 1;
my @op = prand scalar @alg;
for my $i (0..$max){
my $p = $ip[$i];
substr $_, $p, 1, substr $alg[$op[$i]], $p, 1;
}
print;
}
使用示例:
$ cat seq
1634870295
5684937021
2049163587
6598471230
$ cat alg
DPMBHZJEIO
INTMJZOYKQ
KNTXGLCJSR
GLJZRFVSEX
SYJVHEPNAZ
$ perl mutagen 3 alg seq
1L3V8702I5
5684HE7Y21
2049JZC587
6598H7C2E0
如果生成的n
随机数必须不同,则应prand
更改为:
sub prand {
my (@r, $m, %h);
die "more replacements than positions/alignments" if $max >= $_[0];
for(0..$max){
my $r = int(rand() * $_[0]);
$r = ($r + 1) % $_[0] while $h{$r};
$h{$r} = 1;
push @r, $r;
}
@r;
}
启用调试的版本,当给定开关时,它将用颜色漂亮地打印突变-d
:
#! /usr/bin/perl
# usage mutagen [-d] number_of_replacements alignment_file [ sequence_file ..]
use strict;
my $debug = $ARGV[0] eq '-d' ? shift : 0;
my $max = shift() - 1;
my $algf = shift;
open my $alg, $algf or die "open $algf: $!";
my @alg = <$alg>;
sub prand { map int(rand() * $_[0]), 0..$max }
while(<>){
my @ip = prand length() - 1;
my @op = prand scalar @alg;
if($debug){
my $t = ' ' x (length() - 1);
substr $t, $ip[$_], 1, $ip[$_] for 0..$max;
warn "@ip | @op\n $_ $t\n";
for my $i (0..$max){
my $t = $alg[$op[$i]];
$t =~ s/(.{$ip[$i]})(.)/$1\e[1;31m$2\e[m/;
printf STDERR " %2d %s", $op[$i], $t;
}
}
for my $i (0..$max){
my $p = $ip[$i];
substr $_, $p, 1, substr $alg[$op[$i]], $p, 1;
}
print;
if($debug){
my @t = split "", $_;
for my $i (0..$max){
$_ = "\e[1;31m$_\e[m" for $t[$ip[$i]];
}
warn " = ", @t, "\n";
}
}
答案2
该线性将生成无限数量的随机密钥:
cat /dev/urandom | tr -dc 'A-Z0-9' | fold -w 10 | head -n 1
示例输出:
MB0JZZ85VI
2OKOY4JL61
2YN7B71Z6K
KH29TYCQ4K
B4N1XOFY5O
解释:
/dev/random
,/dev/urandom
甚至/dev/arandom
是在系统中充当伪随机数生成器的特殊文件。它们允许访问从设备驱动程序和其他来源收集的环境噪声,可以获得更多信息这里
这折叠命令UNIX 中是一个命令行实用程序,用于折叠指定文件或标准输入的内容。默认情况下,它以 80 列的最大宽度换行。它还支持指定列宽和按字节数换行。w
命令中的标志fold
表示列宽度,它可以间接帮助调整随机生成的密钥中包含的字节数。
命令中的正则表达式控制tr
随机键中包含哪些字符。
head -n
将调整将生成多少个随机密钥。例如,替换-n 1
为10000
将生成 10.000 个密钥。
答案3
bash
由于正在启动的外部进程数量较多,您最初的尝试很慢。每个随机数都被调用jot
,每个字符串操作都使用两个sed
和一个cut
。
当您使用bash
,而不是 pure 时sh
,您可以受益于$随机多变的,子串扩展和数组。这些使得无需外部命令(甚至不需要任何子 shell)即可执行替换bash
。
#!/bin/bash
count=$1
read sequence < $2
IFS=$'\n' read -d '' -a replacements < $3
len=${#sequence}
choices=${#replacements[*]}
while ((count--)) ; do
pos=$(($RANDOM % $len))
choice=$(($RANDOM % $choices))
replacement=${replacements[$choice]}
sequence=${sequence:0:$pos}${replacement:$pos:1}${sequence:$((pos+1))}
done
echo "$sequence"
请注意, $RANDOM 不会超过 32767,因此如果您的序列大于该值(甚至接近该大小),您将需要比$RANDOM % maximum
.
这仍然不可能在速度上击败专用脚本语言,更不用说编译语言了。