Mutagênese aleatória com o bash

4

Eu tenho uma string, por exemplo.

1234567890

e eu quero substituir posições aleatórias dessa string pela posição correspondente de uma sequência aleatória em outro conjunto de outras strings, por exemplo,

ABCDEFGHIJ
KLMNOPQRST
UVWXYZABCD
...

Se eu optar por fazer três substituições, o script deve escolher três números aleatórios, por exemplo: 3,7,8; e 3 sequências aleatórias, e. 1, 1, 3; faça as substituições para gerar a saída esperada:

12C456GB90

Existe uma maneira de fazer isso sem looping significativo? Eu escrevi um script bash simples para gerar uma posição aleatória e uma linha de seqüência aleatória, em seguida, faço uma substituição e, em seguida, repito o processo na saída, repito, repito. Isso funciona perfeitamente, no entanto, em meus arquivos da vida real (muito maiores que os exemplos), quero gerar 10.000 ou mais substituições. Ah, e vou precisar fazer isso várias vezes para gerar várias sequências variantes 'mutadas'.

EDIT: No momento estou usando algo assim:

#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

(com alguns bits adicionais, obviamente) e apenas percorrendo milhares de vezes

    
por catchprj 08.10.2018 / 20:20

3 respostas

0

Nota:

Isto é estritamente para fins de diversão; um programa equivalente em C seria muito mais simples e com ordens de magnitude mais rápidas; quanto ao bash , não vamos nem falar sobre; -)

O seguinte script perl mudará uma lista de sequências de ~ 1M e alinhamentos de ~ 10k em aproximadamente 10 segundos no meu laptop.

#! /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;
}

Exemplo de uso:

$ cat seq
1634870295
5684937021
2049163587
6598471230
$ cat alg
DPMBHZJEIO
INTMJZOYKQ
KNTXGLCJSR
GLJZRFVSEX
SYJVHEPNAZ
$ perl mutagen 3 alg seq
1L3V8702I5
5684HE7Y21
2049JZC587
6598H7C2E0

Se os números aleatórios n gerados tiverem que ser diferentes entre eles, então prand deve ser alterado para:

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;
}

Uma versão habilitada para depuração, que imprimirá a mutação com cores quando for dada a opção -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";
    }
}
    
por 09.10.2018 / 00:58
0

Esse linear geraria um número infinito de chaves aleatórias:

cat /dev/urandom | tr -dc 'A-Z0-9' | fold -w 10 | head -n 1

Exemplo de saída:

MB0JZZ85VI
2OKOY4JL61
2YN7B71Z6K
KH29TYCQ4K
B4N1XOFY5O

Explicação:

/dev/random , /dev/urandom ou mesmo /dev/arandom são arquivos especiais que servem como geradores de números pseudo-aleatórios no sistema. Eles permitem acesso ao ruído ambiental coletado de drivers de dispositivos e outras fontes. Mais informações podem ser obtidas aqui

O comando fold no UNIX é um utilitário de linha de comando para dobrar o conteúdo de arquivos especificados ou entrada padrão. Por padrão, ele envolve linhas em uma largura máxima de 80 colunas. Ele também suporta a especificação da largura da coluna e quebra por números de bytes. O sinalizador w no comando fold representa a largura das colunas e pode ajudar, indiretamente, a ajustar quantos bytes seriam incluídos nas chaves geradas aleatoriamente.

A expressão regex no comando tr controla quais caracteres seriam incluídos nas chaves aleatórias.

head -n ajustaria quantas chaves aleatórias seriam geradas. Por exemplo, substituir -n 1 por 10000 geraria 10.000 chaves.

    
por 08.10.2018 / 21:02
0

Sua tentativa bash original foi lenta devido ao número de processos externos iniciados. Cada número aleatório chamado jot e cada manipulação de string usou dois sed e um cut .

Como você está usando bash , e não sh puro, você pode se beneficiar do $ RANDOM , Expansão da subcadeia e Matrizes . Estes tornam possível realizar as substituições sem comandos externos - nem mesmo qualquer sub-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"

Note que $ RANDOM não irá exceder 32767, então se suas seqüências forem maiores que isso (ou mesmo se aproximando desse tamanho), você precisará de algo mais complexo que $RANDOM % maximum .

Ainda é improvável que ele ultrapasse uma linguagem de script dedicada para velocidade, muito menos para uma linguagem compilada.

    
por 09.10.2018 / 15:32